diff --git a/sentry_sdk/integrations/chalice.py b/sentry_sdk/integrations/chalice.py index 4bc8e3e548..4b59d037aa 100644 --- a/sentry_sdk/integrations/chalice.py +++ b/sentry_sdk/integrations/chalice.py @@ -6,12 +6,10 @@ from sentry_sdk.integrations._wsgi_common import _filter_headers from sentry_sdk.integrations.aws_lambda import _make_request_event_processor from sentry_sdk.traces import ( + SegmentNameSource, SpanStatus, StreamedSpan, - get_current_span, ) -from sentry_sdk.tracing import TransactionSource -from sentry_sdk.tracing_utils import has_span_streaming_enabled from sentry_sdk.utils import ( capture_internal_exceptions, event_from_exception, @@ -79,73 +77,58 @@ def wrapped_view_function(**function_args: "Any") -> "Any": ) ) - if has_span_streaming_enabled(client.options): - current_span = get_current_span() - segment = None - if type(current_span) is StreamedSpan: - # A segment already exists (created by the AWS Lambda - # integration), so decorate it with Chalice attributes - # The AWS Lambda integration owns the span lifecycle - # (end + flush), but Chalice converts unhandled view exceptions - # into 500 responses, so the error must be captured here. - request_dict = app.current_request.to_dict() - headers = request_dict.get("headers", {}) - - header_attrs: "Dict[str, Any]" = {} - for header, value in _filter_headers( - headers, use_annotated_value=False - ).items(): - header_attrs[f"http.request.header.{header.lower()}"] = value - - additional_attrs: "Dict[str, Any]" = {} - if "method" in request_dict: - additional_attrs["http.request.method"] = request_dict["method"] - - attributes = { - "sentry.origin": ChaliceIntegration.origin, - **header_attrs, - **additional_attrs, - } - - segment = current_span._segment - segment.set_attributes(attributes) - - try: - return view_function(**function_args) - except Exception as exc: - if isinstance(exc, ChaliceViewError): - raise - exc_info = sys.exc_info() - if segment: - segment.status = SpanStatus.ERROR.value - sentry_event, hint = event_from_exception( - exc_info, - client_options=client.options, - mechanism={"type": "chalice", "handled": False}, - ) - sentry_sdk.capture_event(sentry_event, hint=hint) - if segment is None: - client.flush() + scope.set_transaction_name( + app.lambda_context.function_name, + source=SegmentNameSource.COMPONENT, + ) + + current_span = sentry_sdk.traces.get_current_span() + segment = None + if type(current_span) is StreamedSpan: + # A segment already exists (created by the AWS Lambda + # integration), so decorate it with Chalice attributes + # The AWS Lambda integration owns the span lifecycle + # (end + flush), but Chalice converts unhandled view exceptions + # into 500 responses, so the error must be captured here. + request_dict = app.current_request.to_dict() + headers = request_dict.get("headers", {}) + + header_attrs: "Dict[str, Any]" = {} + for header, value in _filter_headers( + headers, use_annotated_value=False + ).items(): + header_attrs[f"http.request.header.{header.lower()}"] = value + + additional_attrs: "Dict[str, Any]" = {} + if "method" in request_dict: + additional_attrs["http.request.method"] = request_dict["method"] + + attributes = { + "sentry.origin": ChaliceIntegration.origin, + **header_attrs, + **additional_attrs, + } + + segment = current_span._segment + segment.set_attributes(attributes) + + try: + return view_function(**function_args) + except Exception as exc: + if isinstance(exc, ChaliceViewError): raise - else: - scope.set_transaction_name( - app.lambda_context.function_name, - source=TransactionSource.COMPONENT, + exc_info = sys.exc_info() + if segment: + segment.status = SpanStatus.ERROR.value + sentry_event, hint = event_from_exception( + exc_info, + client_options=client.options, + mechanism={"type": "chalice", "handled": False}, ) - try: - return view_function(**function_args) - except Exception as exc: - if isinstance(exc, ChaliceViewError): - raise - exc_info = sys.exc_info() - sentry_event, hint = event_from_exception( - exc_info, - client_options=client.options, - mechanism={"type": "chalice", "handled": False}, - ) - sentry_sdk.capture_event(sentry_event, hint=hint) + sentry_sdk.capture_event(sentry_event, hint=hint) + if segment is None: client.flush() - raise + raise return wrapped_view_function # type: ignore diff --git a/tests/integrations/chalice/test_chalice.py b/tests/integrations/chalice/test_chalice.py index 69f023efd4..39b55913bd 100644 --- a/tests/integrations/chalice/test_chalice.py +++ b/tests/integrations/chalice/test_chalice.py @@ -41,7 +41,11 @@ def _generate_lambda_context(self): @pytest.fixture def app(sentry_init): - sentry_init(integrations=[ChaliceIntegration()]) + sentry_init( + integrations=[ChaliceIntegration()], + traces_sample_rate=1.0, + trace_lifecycle="stream", + ) app = Chalice(app_name="sentry_chalice") @app.route("/boom") @@ -175,35 +179,9 @@ def test_transaction( assert event["transaction_info"] == {"source": expected_source} -def _make_span_streaming_app(sentry_init): - sentry_init( - integrations=[ChaliceIntegration()], - traces_sample_rate=1.0, - trace_lifecycle="stream", - ) - app = Chalice(app_name="sentry_chalice") - - @app.route("/message") - def hi(): - capture_message("hi") - return {"status": "ok"} - - @app.route("/boom") - def boom(): - raise Exception("boom goes the dynamite!") - - LocalGateway._generate_lambda_context = _generate_lambda_context - - return app - - -def test_span_streaming_existing_span( - sentry_init, - capture_items, -): +def test_existing_span(sentry_init, capture_items, app): """When a segment already exists (e.g. created by the AWS Lambda integration), Chalice decorates it instead of creating a duplicate.""" - app = _make_span_streaming_app(sentry_init) client = RequestHandler(app) items = capture_items("span") @@ -232,11 +210,11 @@ def test_span_streaming_existing_span( assert span["status"] == "ok" -def test_span_streaming_existing_span_error( +def test_existing_span_error( sentry_init, capture_items, + app, ): - app = _make_span_streaming_app(sentry_init) client = RequestHandler(app) items = capture_items("event", "span")