Example #1
0
def test_iter_headers(sentry_init, monkeypatch, tracestate_enabled):
    monkeypatch.setattr(
        Transaction,
        "to_traceparent",
        mock.Mock(
            return_value="12312012123120121231201212312012-0415201309082013-0"
        ),
    )
    monkeypatch.setattr(
        Transaction,
        "to_tracestate",
        mock.Mock(return_value="sentry=doGsaREgReaT,charlie=goofy"),
    )
    monkeypatch.setattr(
        sentry_sdk.tracing,
        "has_tracestate_enabled",
        mock.Mock(return_value=tracestate_enabled),
    )

    transaction = Transaction(
        name="/interactions/other-dogs/new-dog",
        op="greeting.sniff",
    )

    headers = dict(transaction.iter_headers())
    assert (headers["sentry-trace"] ==
            "12312012123120121231201212312012-0415201309082013-0")
    if tracestate_enabled:
        assert "tracestate" in headers
        assert headers["tracestate"] == "sentry=doGsaREgReaT,charlie=goofy"
    else:
        assert "tracestate" not in headers
Example #2
0
    def start_transaction(
            self,
            transaction=None,  # type: Optional[Transaction]
            **kwargs  # type: Any
    ):
        # type: (...) -> Transaction
        """
        Start and return a transaction.

        Start an existing transaction if given, otherwise create and start a new
        transaction with kwargs.

        This is the entry point to manual tracing instrumentation.

        A tree structure can be built by adding child spans to the transaction,
        and child spans to other spans. To start a new child span within the
        transaction or any span, call the respective `.start_child()` method.

        Every child span must be finished before the transaction is finished,
        otherwise the unfinished spans are discarded.

        When used as context managers, spans and transactions are automatically
        finished at the end of the `with` block. If not using context managers,
        call the `.finish()` method.

        When the transaction is finished, it will be sent to Sentry with all its
        finished child spans.
        """
        custom_sampling_context = kwargs.pop("custom_sampling_context", {})

        # if we haven't been given a transaction, make one
        if transaction is None:
            kwargs.setdefault("hub", self)
            transaction = Transaction(**kwargs)

        # use traces_sample_rate, traces_sampler, and/or inheritance to make a
        # sampling decision
        sampling_context = {
            "transaction_context": transaction.to_json(),
            "parent_sampled": transaction.parent_sampled,
        }
        sampling_context.update(custom_sampling_context)
        transaction._set_initial_sampling_decision(
            sampling_context=sampling_context)

        # we don't bother to keep spans if we already know we're not going to
        # send the transaction
        if transaction.sampled:
            max_spans = (
                self.client and
                self.client.options["_experiments"].get("max_spans")) or 1000
            transaction.init_span_recorder(maxlen=max_spans)

        return transaction
Example #3
0
def test_tracestate_is_immutable_once_set(sentry_init, monkeypatch, set_by):
    monkeypatch.setattr(
        sentry_sdk.tracing,
        "compute_tracestate_entry",
        mock.Mock(return_value="sentry=doGsaREgReaT"),
    )

    sentry_init(
        dsn=
        "https://[email protected]/12312012",
        environment="dogpark",
        release="off.leash.park",
    )

    # for each scenario, get to the point where tracestate has been set
    if set_by == "inheritance":
        transaction = Transaction(
            name="/interactions/other-dogs/new-dog",
            op="greeting.sniff",
            sentry_tracestate=("sentry=doGsaREgReaT"),
        )
    else:
        transaction = Transaction(
            name="/interactions/other-dogs/new-dog",
            op="greeting.sniff",
        )

        if set_by == "to_tracestate":
            transaction.to_tracestate()
        if set_by == "get_trace_context":
            transaction.get_trace_context()

    assert transaction._sentry_tracestate == "sentry=doGsaREgReaT"

    # user data would be included in tracestate if it were recomputed at this point
    sentry_sdk.set_user({"id": 12312013, "segment": "bigs"})

    # value hasn't changed
    assert transaction._sentry_tracestate == "sentry=doGsaREgReaT"
Example #4
0
def test_to_tracestate(sentry_init):
    sentry_init(
        dsn=
        "https://[email protected]/12312012",
        environment="dogpark",
        release="off.leash.park",
    )

    # it correctly uses the value from the transaction itself or the span's
    # containing transaction
    transaction_no_third_party = Transaction(
        trace_id="12312012123120121231201212312012",
        sentry_tracestate="sentry=doGsaREgReaT",
    )
    non_orphan_span = Span()
    non_orphan_span._containing_transaction = transaction_no_third_party
    assert transaction_no_third_party.to_tracestate() == "sentry=doGsaREgReaT"
    assert non_orphan_span.to_tracestate() == "sentry=doGsaREgReaT"

    # it combines sentry and third-party values correctly
    transaction_with_third_party = Transaction(
        trace_id="12312012123120121231201212312012",
        sentry_tracestate="sentry=doGsaREgReaT",
        third_party_tracestate="maisey=silly",
    )
    assert (transaction_with_third_party.to_tracestate() ==
            "sentry=doGsaREgReaT,maisey=silly")

    # it computes a tracestate from scratch for orphan transactions
    orphan_span = Span(trace_id="12312012123120121231201212312012", )
    assert orphan_span._containing_transaction is None
    assert orphan_span.to_tracestate() == "sentry=" + compute_tracestate_value(
        {
            "trace_id": "12312012123120121231201212312012",
            "environment": "dogpark",
            "release": "off.leash.park",
            "public_key": "dogsarebadatkeepingsecrets",
        })
Example #5
0
    async def _run_app(self, scope, callback):
        # type: (Any, Any) -> Any
        is_recursive_asgi_middleware = _asgi_middleware_applied.get(False)

        if is_recursive_asgi_middleware:
            try:
                return await callback()
            except Exception as exc:
                _capture_exception(Hub.current, exc)
                raise exc from None

        _asgi_middleware_applied.set(True)
        try:
            hub = Hub(Hub.current)
            with auto_session_tracking(hub, session_mode="request"):
                with hub:
                    with hub.configure_scope() as sentry_scope:
                        sentry_scope.clear_breadcrumbs()
                        sentry_scope._name = "asgi"
                        processor = partial(self.event_processor,
                                            asgi_scope=scope)
                        sentry_scope.add_event_processor(processor)

                    ty = scope["type"]

                    if ty in ("http", "websocket"):
                        transaction = Transaction.continue_from_headers(
                            self._get_headers(scope),
                            op="{}.server".format(ty),
                        )
                    else:
                        transaction = Transaction(op="asgi.server")

                    transaction.name = _DEFAULT_TRANSACTION_NAME
                    transaction.set_tag("asgi.type", ty)

                    with hub.start_transaction(
                            transaction,
                            custom_sampling_context={"asgi_scope": scope}):
                        # XXX: Would be cool to have correct span status, but we
                        # would have to wrap send(). That is a bit hard to do with
                        # the current abstraction over ASGI 2/3.
                        try:
                            return await callback()
                        except Exception as exc:
                            _capture_exception(hub, exc)
                            raise exc from None
        finally:
            _asgi_middleware_applied.set(False)
Example #6
0
def test_start_transaction(sentry_init):
    sentry_init(traces_sample_rate=1.0)

    # you can have it start a transaction for you
    result1 = start_transaction(name="/interactions/other-dogs/new-dog",
                                op="greeting.sniff")
    assert isinstance(result1, Transaction)
    assert result1.name == "/interactions/other-dogs/new-dog"
    assert result1.op == "greeting.sniff"

    # or you can pass it an already-created transaction
    preexisting_transaction = Transaction(
        name="/interactions/other-dogs/new-dog", op="greeting.sniff")
    result2 = start_transaction(preexisting_transaction)
    assert result2 is preexisting_transaction
Example #7
0
def test_doesnt_add_new_tracestate_to_transaction_when_none_given(sentry_init):
    sentry_init(
        dsn=
        "https://[email protected]/12312012",
        environment="dogpark",
        release="off.leash.park",
    )

    transaction = Transaction(
        name="/interactions/other-dogs/new-dog",
        op="greeting.sniff",
        # sentry_tracestate=< value would be passed here >
    )

    assert transaction._sentry_tracestate is None
Example #8
0
def test_to_traceparent(sentry_init, sampled):

    transaction = Transaction(
        name="/interactions/other-dogs/new-dog",
        op="greeting.sniff",
        trace_id="12312012123120121231201212312012",
        sampled=sampled,
    )

    traceparent = transaction.to_traceparent()

    trace_id, parent_span_id, parent_sampled = traceparent.split("-")
    assert trace_id == "12312012123120121231201212312012"
    assert parent_span_id == transaction.span_id
    assert parent_sampled == ("1" if sampled is True else
                              "0" if sampled is False else "")
Example #9
0
    def start_transaction(
        self,
        transaction=None,  # type: Optional[Transaction]
        **kwargs  # type: Any
    ):
        # type: (...) -> Transaction
        """
        Start and return a transaction.

        Start an existing transaction if given, otherwise create and start a new
        transaction with kwargs.

        This is the entry point to manual tracing instrumentation.

        A tree structure can be built by adding child spans to the transaction,
        and child spans to other spans. To start a new child span within the
        transaction or any span, call the respective `.start_child()` method.

        Every child span must be finished before the transaction is finished,
        otherwise the unfinished spans are discarded.

        When used as context managers, spans and transactions are automatically
        finished at the end of the `with` block. If not using context managers,
        call the `.finish()` method.

        When the transaction is finished, it will be sent to Sentry with all its
        finished child spans.
        """
        if transaction is None:
            kwargs.setdefault("hub", self)
            transaction = Transaction(**kwargs)

        client, scope = self._stack[-1]

        if transaction.sampled is None:
            sample_rate = client and client.options["traces_sample_rate"] or 0
            transaction.sampled = random.random() < sample_rate

        if transaction.sampled:
            max_spans = (
                client and client.options["_experiments"].get("max_spans") or 1000
            )
            transaction.init_span_recorder(maxlen=max_spans)

        return transaction
Example #10
0
def test_adds_tracestate_to_transaction_when_getting_trace_context(
        sentry_init):
    sentry_init(
        dsn=
        "https://[email protected]/12312012",
        environment="dogpark",
        release="off.leash.park",
    )

    transaction = Transaction(
        name="/interactions/other-dogs/new-dog",
        op="greeting.sniff",
    )

    # no inherited tracestate, and none created in Transaction constructor
    assert transaction._sentry_tracestate is None

    transaction.get_trace_context()

    assert transaction._sentry_tracestate is not None
def test_transaction_method_signature(sentry_init, capture_events):
    sentry_init(traces_sample_rate=1.0)
    events = capture_events()

    with pytest.raises(TypeError):
        start_span(name="foo")
    assert len(events) == 0

    with start_transaction() as transaction:
        pass
    assert transaction.name == "<unlabeled transaction>"
    assert len(events) == 1

    with start_transaction() as transaction:
        transaction.name = "name-known-after-transaction-started"
    assert len(events) == 2

    with start_transaction(name="a"):
        pass
    assert len(events) == 3

    with start_transaction(Transaction(name="c")):
        pass
    assert len(events) == 4
Example #12
0
def test_tracestate_computation(sentry_init):
    sentry_init(
        dsn=
        "https://[email protected]/12312012",
        environment="dogpark",
        release="off.leash.park",
    )

    sentry_sdk.set_user({"id": 12312013, "segment": "bigs"})

    transaction = Transaction(
        name="/interactions/other-dogs/new-dog",
        op="greeting.sniff",
        trace_id="12312012123120121231201212312012",
    )

    # force lazy computation to create a value
    transaction.to_tracestate()

    computed_value = transaction._sentry_tracestate.replace("sentry=", "")
    # we have to decode and reinflate the data because we can guarantee that the
    # order of the entries in the jsonified dict will be the same here as when
    # the tracestate is computed
    reinflated_trace_data = json.loads(from_base64(computed_value))

    assert reinflated_trace_data == {
        "trace_id": "12312012123120121231201212312012",
        "environment": "dogpark",
        "release": "off.leash.park",
        "public_key": "dogsarebadatkeepingsecrets",
        "user": {
            "id": 12312013,
            "segment": "bigs"
        },
        "transaction": "/interactions/other-dogs/new-dog",
    }