예제 #1
0
    def __call__(self, environ, start_response):
        # type: (Dict[str, str], Callable[..., Any]) -> _ScopedResponse
        if _wsgi_middleware_applied.get(False):
            return self.app(environ, start_response)

        _wsgi_middleware_applied.set(True)
        try:
            hub = Hub(Hub.current)
            with auto_session_tracking(hub, session_mode="request"):
                with hub:
                    with capture_internal_exceptions():
                        with hub.configure_scope() as scope:
                            scope.clear_breadcrumbs()
                            scope._name = "wsgi"
                            scope.add_event_processor(
                                _make_wsgi_event_processor(environ))

                    transaction = Transaction.continue_from_environ(
                        environ, op="http.server", name="generic WSGI request")

                    with hub.start_transaction(
                            transaction,
                            custom_sampling_context={"wsgi_environ": environ}):
                        try:
                            rv = self.app(
                                environ,
                                partial(_sentry_start_response, start_response,
                                        transaction),
                            )
                        except BaseException:
                            reraise(*_capture_exception(hub))
        finally:
            _wsgi_middleware_applied.set(False)

        return _ScopedResponse(hub, rv)
예제 #2
0
    def _inner(*args, **kwargs):
        # type: (*Any, **Any) -> Any
        hub = Hub.current
        if hub.get_integration(CeleryIntegration) is None:
            return f(*args, **kwargs)

        with hub.push_scope() as scope:
            scope._name = "celery"
            scope.clear_breadcrumbs()
            scope.add_event_processor(
                _make_event_processor(task, *args, **kwargs))

            transaction = Transaction.continue_from_headers(
                args[3].get("headers") or {},
                op="celery.task",
                name="unknown celery task",
            )

            # Could possibly use a better hook than this one
            transaction.set_status("ok")

            with capture_internal_exceptions():
                # Celery task objects are not a thing to be trusted. Even
                # something such as attribute access can fail.
                transaction.name = task.name

            with hub.start_transaction(transaction):
                return f(*args, **kwargs)
예제 #3
0
def _handle_request_impl(self):
    # type: (RequestHandler) -> Generator[None, None, None]
    hub = Hub.current
    integration = hub.get_integration(TornadoIntegration)

    if integration is None:
        yield

    weak_handler = weakref.ref(self)

    with Hub(hub) as hub:
        with hub.configure_scope() as scope:
            scope.clear_breadcrumbs()
            processor = _make_event_processor(weak_handler)  # type: ignore
            scope.add_event_processor(processor)

        transaction = Transaction.continue_from_headers(
            self.request.headers,
            op="http.server",
            # Like with all other integrations, this is our
            # fallback transaction in case there is no route.
            # sentry_urldispatcher_resolve is responsible for
            # setting a transaction name later.
            name="generic Tornado request",
        )

        with hub.start_transaction(
            transaction, custom_sampling_context={"tornado_request": self.request}
        ):
            yield
def test_continue_from_headers(sentry_init, capture_events, sampled,
                               sample_rate):
    """
    Ensure data is actually passed along via headers, and that they are read
    correctly.
    """
    sentry_init(traces_sample_rate=sample_rate)
    events = capture_events()

    # make a parent transaction (normally this would be in a different service)
    with start_transaction(
            name="hi",
            sampled=True if sample_rate == 0 else None) as parent_transaction:
        with start_span() as old_span:
            old_span.sampled = sampled
            headers = dict(
                Hub.current.iter_trace_propagation_headers(old_span))
            tracestate = parent_transaction._sentry_tracestate

    # child transaction, to prove that we can read 'sentry-trace' and
    # `tracestate` header data correctly
    child_transaction = Transaction.continue_from_headers(headers,
                                                          name="WRONG")
    assert child_transaction is not None
    assert child_transaction.parent_sampled == sampled
    assert child_transaction.trace_id == old_span.trace_id
    assert child_transaction.same_process_as_parent is False
    assert child_transaction.parent_span_id == old_span.span_id
    assert child_transaction.span_id != old_span.span_id
    assert child_transaction._sentry_tracestate == tracestate

    # add child transaction to the scope, to show that the captured message will
    # be tagged with the trace id (since it happens while the transaction is
    # open)
    with start_transaction(child_transaction):
        with configure_scope() as scope:
            # change the transaction name from "WRONG" to make sure the change
            # is reflected in the final data
            scope.transaction = "ho"
        capture_message("hello")

    # in this case the child transaction won't be captured
    if sampled is False or (sample_rate == 0 and sampled is None):
        trace1, message = events

        assert trace1["transaction"] == "hi"
    else:
        trace1, message, trace2 = events

        assert trace1["transaction"] == "hi"
        assert trace2["transaction"] == "ho"

        assert (trace1["contexts"]["trace"]["trace_id"] ==
                trace2["contexts"]["trace"]["trace_id"] ==
                child_transaction.trace_id ==
                message["contexts"]["trace"]["trace_id"])

    assert message["message"] == "hello"
예제 #5
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
예제 #6
0
    def sentry_handler(event, context, *args, **kwargs):
        # type: (Any, Any, *Any, **Any) -> Any
        hub = Hub.current
        integration = hub.get_integration(AwsLambdaIntegration)
        if integration is None:
            return handler(event, context, *args, **kwargs)

        # If an integration is there, a client has to be there.
        client = hub.client  # type: Any
        configured_time = context.get_remaining_time_in_millis()

        with hub.push_scope() as scope:
            with capture_internal_exceptions():
                scope.clear_breadcrumbs()
                scope.add_event_processor(
                    _make_request_event_processor(event, context,
                                                  configured_time))
                scope.set_tag("aws_region",
                              context.invoked_function_arn.split(":")[3])

                timeout_thread = None
                # Starting the Timeout thread only if the configured time is greater than Timeout warning
                # buffer and timeout_warning parameter is set True.
                if (integration.timeout_warning
                        and configured_time > TIMEOUT_WARNING_BUFFER):
                    waiting_time = (configured_time -
                                    TIMEOUT_WARNING_BUFFER) / MILLIS_TO_SECONDS

                    timeout_thread = TimeoutThread(
                        waiting_time,
                        configured_time / MILLIS_TO_SECONDS,
                    )

                    # Starting the thread to raise timeout warning exception
                    timeout_thread.start()

            headers = event.get("headers", {})
            transaction = Transaction.continue_from_headers(
                headers, op="serverless.function", name=context.function_name)
            with hub.start_transaction(transaction):
                try:
                    return handler(event, context, *args, **kwargs)
                except Exception:
                    exc_info = sys.exc_info()
                    event, hint = event_from_exception(
                        exc_info,
                        client_options=client.options,
                        mechanism={
                            "type": "aws_lambda",
                            "handled": False
                        },
                    )
                    hub.capture_event(event, hint=hint)
                    reraise(*exc_info)
                finally:
                    if timeout_thread:
                        timeout_thread.stop()
def test_continue_from_headers(sentry_init, capture_events, sampled, sample_rate):
    sentry_init(traces_sample_rate=sample_rate)
    events = capture_events()

    # make a parent transaction (normally this would be in a different service)
    with start_transaction(name="hi", sampled=True if sample_rate == 0 else None):
        with start_span() as old_span:
            old_span.sampled = sampled
            headers = dict(Hub.current.iter_trace_propagation_headers(old_span))

    # test that the sampling decision is getting encoded in the header correctly
    header = headers["sentry-trace"]
    if sampled is True:
        assert header.endswith("-1")
    if sampled is False:
        assert header.endswith("-0")
    if sampled is None:
        assert header.endswith("-")

    # child transaction, to prove that we can read 'sentry-trace' header data
    # correctly
    transaction = Transaction.continue_from_headers(headers, name="WRONG")
    assert transaction is not None
    assert transaction.parent_sampled == sampled
    assert transaction.trace_id == old_span.trace_id
    assert transaction.same_process_as_parent is False
    assert transaction.parent_span_id == old_span.span_id
    assert transaction.span_id != old_span.span_id

    # add child transaction to the scope, to show that the captured message will
    # be tagged with the trace id (since it happens while the transaction is
    # open)
    with start_transaction(transaction):
        with configure_scope() as scope:
            scope.transaction = "ho"
        capture_message("hello")

    if sampled is False or (sample_rate == 0 and sampled is None):
        trace1, message = events

        assert trace1["transaction"] == "hi"
    else:
        trace1, message, trace2 = events

        assert trace1["transaction"] == "hi"
        assert trace2["transaction"] == "ho"

        assert (
            trace1["contexts"]["trace"]["trace_id"]
            == trace2["contexts"]["trace"]["trace_id"]
            == transaction.trace_id
            == message["contexts"]["trace"]["trace_id"]
        )

    assert message["message"] == "hello"
예제 #8
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
예제 #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.
        """
        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
예제 #10
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
예제 #11
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",
    }
예제 #12
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
예제 #13
0
def test_continue_from_headers(sentry_init, capture_events, sampled):
    sentry_init(traces_sample_rate=1.0, traceparent_v2=True)
    events = capture_events()

    with start_transaction(name="hi"):
        with start_span() as old_span:
            old_span.sampled = sampled
            headers = dict(Hub.current.iter_trace_propagation_headers())

    header = headers["sentry-trace"]
    if sampled is True:
        assert header.endswith("-1")
    if sampled is False:
        assert header.endswith("-0")
    if sampled is None:
        assert header.endswith("-")

    transaction = Transaction.continue_from_headers(headers, name="WRONG")
    assert transaction is not None
    assert transaction.sampled == sampled
    assert transaction.trace_id == old_span.trace_id
    assert transaction.same_process_as_parent is False
    assert transaction.parent_span_id == old_span.span_id
    assert transaction.span_id != old_span.span_id

    with start_transaction(transaction):
        with configure_scope() as scope:
            scope.transaction = "ho"
        capture_message("hello")

    if sampled is False:
        trace1, message = events

        assert trace1["transaction"] == "hi"
    else:
        trace1, message, trace2 = events

        assert trace1["transaction"] == "hi"
        assert trace2["transaction"] == "ho"

        assert (
            trace1["contexts"]["trace"]["trace_id"]
            == trace2["contexts"]["trace"]["trace_id"]
            == transaction.trace_id
            == message["contexts"]["trace"]["trace_id"]
        )

    assert message["message"] == "hello"
예제 #14
0
    async def wrap(*args, **kwargs):
        with Hub(Hub.current) as hub:
            with hub.push_scope() as scope:
                scope.add_event_processor(_process_ws)
                for key, builder in ext._scopes.items():
                    scope.set_extra(key, await builder())

                txn = Transaction.continue_from_headers(
                    current.request._scope["headers"], op="websocket.server")
                txn.set_tag("asgi.type", "websocket")

                with hub.start_transaction(txn):
                    try:
                        return await dispatch_method(*args, **kwargs)
                    except Exception as exc:
                        _capture_exception(hub, exc)
                        raise
예제 #15
0
    def _inner(*args, **kwargs):
        # type: (*Any, **Any) -> Any
        hub = Hub.current
        if hub.get_integration(CeleryIntegration) is None:
            return f(*args, **kwargs)

        with hub.push_scope() as scope:
            scope._name = "celery"
            scope.clear_breadcrumbs()
            scope.add_event_processor(
                _make_event_processor(task, *args, **kwargs))

            transaction = None

            # Celery task objects are not a thing to be trusted. Even
            # something such as attribute access can fail.
            with capture_internal_exceptions():
                transaction = Transaction.continue_from_headers(
                    args[3].get("headers") or {},
                    op="celery.task",
                    name="unknown celery task",
                )

                transaction.name = task.name
                transaction.set_status("ok")

            if transaction is None:
                return f(*args, **kwargs)

            with hub.start_transaction(
                    transaction,
                    custom_sampling_context=
                {
                    "celery_job": {
                        "task": task.name,
                        # for some reason, args[1] is a list if non-empty but a
                        # tuple if empty
                        "args": list(args[1]),
                        "kwargs": args[2],
                    }
                },
            ):
                return f(*args, **kwargs)
예제 #16
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"
예제 #17
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)
예제 #18
0
        async def sentry_app_handle(self, request, *args, **kwargs):
            # type: (Any, Request, *Any, **Any) -> Any
            hub = Hub.current
            if hub.get_integration(AioHttpIntegration) is None:
                return await old_handle(self, request, *args, **kwargs)

            weak_request = weakref.ref(request)

            with Hub(hub) as hub:
                # Scope data will not leak between requests because aiohttp
                # create a task to wrap each request.
                with hub.configure_scope() as scope:
                    scope.clear_breadcrumbs()
                    scope.add_event_processor(
                        _make_request_processor(weak_request))

                transaction = Transaction.continue_from_headers(
                    request.headers,
                    op="http.server",
                    # If this transaction name makes it to the UI, AIOHTTP's
                    # URL resolver did not find a route or died trying.
                    name="generic AIOHTTP request",
                )
                with hub.start_transaction(
                        transaction,
                        custom_sampling_context={"aiohttp_request": request}):
                    try:
                        response = await old_handle(self, request)
                    except HTTPException as e:
                        transaction.set_http_status(e.status_code)
                        raise
                    except (asyncio.CancelledError, ConnectionResetError):
                        transaction.set_status("cancelled")
                        raise
                    except Exception:
                        # This will probably map to a 500 but seems like we
                        # have no way to tell. Do not set span status.
                        reraise(*_capture_exception(hub))

                    transaction.set_http_status(response.status)
                    return response
예제 #19
0
def test_passes_parent_sampling_decision_in_sampling_context(
        sentry_init, parent_sampling_decision):
    sentry_init(traces_sample_rate=1.0)

    sentry_trace_header = (
        "12312012123120121231201212312012-1121201211212012-{sampled}".format(
            sampled=int(parent_sampling_decision)))

    transaction = Transaction.from_traceparent(sentry_trace_header,
                                               name="dogpark")
    spy = mock.Mock(wraps=transaction)
    start_transaction(transaction=spy)

    # there's only one call (so index at 0) and kwargs are always last in a call
    # tuple (so index at -1)
    sampling_context = spy._set_initial_sampling_decision.mock_calls[0][-1][
        "sampling_context"]
    assert "parent_sampled" in sampling_context
    # because we passed in a spy, attribute access requires unwrapping
    assert sampling_context[
        "parent_sampled"]._mock_wraps is parent_sampling_decision
예제 #20
0
    async def middleware(self, handler: ASGIApp, request: Request,
                         receive: Receive, send: Send):  # type: ignore
        """Capture exceptions to Sentry."""
        hub = Hub(Hub.current)
        with hub.configure_scope() as scope:
            scope.clear_breadcrumbs()
            scope._name = "muffin"
            self.current_scope.set(scope)
            scope.add_event_processor(
                partial(self.processData, request=request))

            with hub.start_transaction(
                    Transaction.continue_from_headers(
                        request.headers, op=f"{request.scope['type']}.muffin"),
                    custom_sampling_context={'asgi_scope': scope}):
                try:
                    return await handler(request, receive, send)

                except Exception as exc:
                    if type(exc) not in self.cfg.ignore_errors:
                        hub.capture_exception(exc)
                    raise exc from None
예제 #21
0
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
예제 #22
0
        def sentry_patched_perform_job(self, job, *args, **kwargs):
            # type: (Any, Job, *Queue, **Any) -> bool
            hub = Hub.current
            integration = hub.get_integration(RqIntegration)

            if integration is None:
                return old_perform_job(self, job, *args, **kwargs)

            client = hub.client
            assert client is not None

            with hub.push_scope() as scope:
                scope.clear_breadcrumbs()
                scope.add_event_processor(_make_event_processor(weakref.ref(job)))

                transaction = Transaction.continue_from_headers(
                    job.meta.get("_sentry_trace_headers") or {},
                    op="rq.task",
                    name="unknown RQ task",
                )

                with capture_internal_exceptions():
                    transaction.name = job.func_name

                with hub.start_transaction(
                    transaction, custom_sampling_context={"rq_job": job}
                ):
                    rv = old_perform_job(self, job, *args, **kwargs)

            if self.is_horse:
                # We're inside of a forked process and RQ is
                # about to call `os._exit`. Make sure that our
                # events get sent out.
                client.flush()

            return rv
예제 #23
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",
        })
예제 #24
0
    def sentry_func(functionhandler, event, *args, **kwargs):
        # type: (Any, Any, *Any, **Any) -> Any

        hub = Hub.current
        integration = hub.get_integration(GcpIntegration)
        if integration is None:
            return func(functionhandler, event, *args, **kwargs)

        # If an integration is there, a client has to be there.
        client = hub.client  # type: Any

        configured_time = environ.get("FUNCTION_TIMEOUT_SEC")
        if not configured_time:
            logger.debug(
                "The configured timeout could not be fetched from Cloud Functions configuration."
            )
            return func(functionhandler, event, *args, **kwargs)

        configured_time = int(configured_time)

        initial_time = datetime.utcnow()

        with hub.push_scope() as scope:
            with capture_internal_exceptions():
                scope.clear_breadcrumbs()
                scope.add_event_processor(
                    _make_request_event_processor(event, configured_time, initial_time)
                )
                scope.set_tag("gcp_region", environ.get("FUNCTION_REGION"))
                if (
                    integration.timeout_warning
                    and configured_time > TIMEOUT_WARNING_BUFFER
                ):
                    waiting_time = configured_time - TIMEOUT_WARNING_BUFFER

                    timeout_thread = TimeoutThread(waiting_time, configured_time)

                    # Starting the thread to raise timeout warning exception
                    timeout_thread.start()

            headers = {}
            if hasattr(event, "headers"):
                headers = event.headers
            transaction = Transaction.continue_from_headers(
                headers, op="serverless.function", name=environ.get("FUNCTION_NAME", "")
            )
            with hub.start_transaction(transaction):
                try:
                    return func(functionhandler, event, *args, **kwargs)
                except Exception:
                    exc_info = sys.exc_info()
                    event, hint = event_from_exception(
                        exc_info,
                        client_options=client.options,
                        mechanism={"type": "gcp", "handled": False},
                    )
                    hub.capture_event(event, hint=hint)
                    reraise(*exc_info)
                finally:
                    # Flush out the event queue
                    hub.flush()
예제 #25
0
    def sentry_handler(aws_event, aws_context, *args, **kwargs):
        # type: (Any, Any, *Any, **Any) -> Any

        # Per https://docs.aws.amazon.com/lambda/latest/dg/python-handler.html,
        # `event` here is *likely* a dictionary, but also might be a number of
        # other types (str, int, float, None).
        #
        # In some cases, it is a list (if the user is batch-invoking their
        # function, for example), in which case we'll use the first entry as a
        # representative from which to try pulling request data. (Presumably it
        # will be the same for all events in the list, since they're all hitting
        # the lambda in the same request.)

        if isinstance(aws_event, list):
            request_data = aws_event[0]
            batch_size = len(aws_event)
        else:
            request_data = aws_event
            batch_size = 1

        if not isinstance(request_data, dict):
            # If we're not dealing with a dictionary, we won't be able to get
            # headers, path, http method, etc in any case, so it's fine that
            # this is empty
            request_data = {}

        hub = Hub.current
        integration = hub.get_integration(AwsLambdaIntegration)
        if integration is None:
            return handler(aws_event, aws_context, *args, **kwargs)

        # If an integration is there, a client has to be there.
        client = hub.client  # type: Any
        configured_time = aws_context.get_remaining_time_in_millis()

        with hub.push_scope() as scope:
            with capture_internal_exceptions():
                scope.clear_breadcrumbs()
                scope.add_event_processor(
                    _make_request_event_processor(request_data, aws_context,
                                                  configured_time))
                scope.set_tag("aws_region",
                              aws_context.invoked_function_arn.split(":")[3])
                if batch_size > 1:
                    scope.set_tag("batch_request", True)
                    scope.set_tag("batch_size", batch_size)

                timeout_thread = None
                # Starting the Timeout thread only if the configured time is greater than Timeout warning
                # buffer and timeout_warning parameter is set True.
                if (integration.timeout_warning
                        and configured_time > TIMEOUT_WARNING_BUFFER):
                    waiting_time = (configured_time -
                                    TIMEOUT_WARNING_BUFFER) / MILLIS_TO_SECONDS

                    timeout_thread = TimeoutThread(
                        waiting_time,
                        configured_time / MILLIS_TO_SECONDS,
                    )

                    # Starting the thread to raise timeout warning exception
                    timeout_thread.start()

            headers = request_data.get("headers", {})
            transaction = Transaction.continue_from_headers(
                headers,
                op="serverless.function",
                name=aws_context.function_name)
            with hub.start_transaction(
                    transaction,
                    custom_sampling_context={
                        "aws_event": aws_event,
                        "aws_context": aws_context,
                    },
            ):
                try:
                    return handler(aws_event, aws_context, *args, **kwargs)
                except Exception:
                    exc_info = sys.exc_info()
                    sentry_event, hint = event_from_exception(
                        exc_info,
                        client_options=client.options,
                        mechanism={
                            "type": "aws_lambda",
                            "handled": False
                        },
                    )
                    hub.capture_event(sentry_event, hint=hint)
                    reraise(*exc_info)
                finally:
                    if timeout_thread:
                        timeout_thread.stop()
예제 #26
0
def _start_transaction(**kwargs):
    transaction = Transaction.continue_from_headers(
        dict(Hub.current.iter_trace_propagation_headers()), **kwargs
    )
    transaction.same_process_as_parent = True
    return sentry_sdk.start_transaction(transaction)