diff --git a/sentry_sdk/integrations/bottle.py b/sentry_sdk/integrations/bottle.py index a973e6fbe3..51c9ba6d2e 100644 --- a/sentry_sdk/integrations/bottle.py +++ b/sentry_sdk/integrations/bottle.py @@ -192,29 +192,39 @@ def size_of_file(self, file: "FileUpload") -> int: return file.content_length -def _set_segment_name_and_source(transaction_style: str) -> None: +def _get_transaction_name(transaction_style: str, request: "Any") -> str: try: if transaction_style == "url": - name = bottle_request.route.rule or "bottle request" + return request.route.rule or "bottle request" else: - name = ( - bottle_request.route.name - or transaction_from_function(bottle_request.route.callback) + return ( + request.route.name + or transaction_from_function(request.route.callback) or "bottle request" ) + except RuntimeError: + return "bottle request" + +def _set_segment_name_and_source(transaction_style: str) -> None: + name = _get_transaction_name(transaction_style, bottle_request) + if name: sentry_sdk.get_current_scope().set_transaction_name( name, source=SEGMENT_SOURCE_FOR_STYLE[transaction_style], ) - except RuntimeError: - pass def _make_request_event_processor( app: "Bottle", request: "LocalRequest", integration: "BottleIntegration" ) -> "EventProcessor": def event_processor(event: "Event", hint: "dict[str, Any]") -> "Event": + name = _get_transaction_name(integration.transaction_style, request) + event["transaction"] = name + event["transaction_info"] = { + "source": SEGMENT_SOURCE_FOR_STYLE[integration.transaction_style] + } + with capture_internal_exceptions(): BottleRequestExtractor(request).extract_into_event(event) diff --git a/tests/integrations/bottle/test_bottle.py b/tests/integrations/bottle/test_bottle.py index 0c4648bf2e..9798e8ded0 100644 --- a/tests/integrations/bottle/test_bottle.py +++ b/tests/integrations/bottle/test_bottle.py @@ -824,6 +824,34 @@ def index(): assert "data" not in event.get("_meta", {}).get("request", {}) +def test_error_event_has_transaction_name(sentry_init, capture_events): + """ + Error events captured during request handling should have the correct + transaction name set by the event processor, even though the inline + set_transaction_name call happens after the handler returns. + """ + sentry_init(integrations=[BottleIntegration(transaction_style="url")]) + events = capture_events() + + app = Bottle() + + @app.route("/error/") + def error_handler(error_id): + 1 / 0 + + client = Client(app) + + try: + client.get("/error/123") + except ZeroDivisionError: + pass + + (event,) = events + assert event["exception"]["values"][0]["type"] == "ZeroDivisionError" + assert event["transaction"] == "/error/" + assert event["transaction_info"] == {"source": "route"} + + def test_oversized_request_body_not_annotated_data_collection( sentry_init, capture_events, app, get_client ):