From 8121c6dfd18599972a936ded58e251f5f13a6b1e Mon Sep 17 00:00:00 2001 From: Alexander Alderman Webb Date: Wed, 10 Jun 2026 12:47:58 +0200 Subject: [PATCH 1/2] feat(litestar): Set name and source on route span when streaming --- sentry_sdk/integrations/litestar.py | 49 +++++++-------- tests/integrations/litestar/test_litestar.py | 63 ++++++++++++++++++++ 2 files changed, 86 insertions(+), 26 deletions(-) diff --git a/sentry_sdk/integrations/litestar.py b/sentry_sdk/integrations/litestar.py index 6f05144e78..e13ddf99d8 100644 --- a/sentry_sdk/integrations/litestar.py +++ b/sentry_sdk/integrations/litestar.py @@ -287,9 +287,29 @@ async def handle_wrapper( request_data = await body - def event_processor(event: "Event", _: "Hint") -> "Event": - route_handler = scope.get("route_handler") + route_handler = scope.get("route_handler") + + func = None + if route_handler.name is not None: + name = route_handler.name + # Accounts for use of type `Ref` in earlier versions of litestar without the need to reference it as a type + elif hasattr(route_handler.fn, "value"): + func = route_handler.fn.value + else: + func = route_handler.fn + if func is not None: + name = transaction_from_function(func) + + source = SOURCE_FOR_STYLE["endpoint"] + if not name: + name = _DEFAULT_TRANSACTION_NAME + source = TransactionSource.ROUTE.value + + sentry_sdk.set_transaction_name(name, source) + sentry_scope.set_transaction_name(name, source) + + def event_processor(event: "Event", _: "Hint") -> "Event": request_info = event.get("request", {}) request_info["content_length"] = len(scope.get("_body", b"")) if should_send_default_pii(): @@ -297,30 +317,7 @@ def event_processor(event: "Event", _: "Hint") -> "Event": if request_data is not None: request_info["data"] = request_data - func = None - if route_handler.name is not None: - tx_name = route_handler.name - # Accounts for use of type `Ref` in earlier versions of litestar without the need to reference it as a type - elif hasattr(route_handler.fn, "value"): - func = route_handler.fn.value - else: - func = route_handler.fn - if func is not None: - tx_name = transaction_from_function(func) - - tx_info = {"source": SOURCE_FOR_STYLE["endpoint"]} - - if not tx_name: - tx_name = _DEFAULT_TRANSACTION_NAME - tx_info = {"source": TransactionSource.ROUTE} - - event.update( - { - "request": deepcopy(request_info), - "transaction": tx_name, - "transaction_info": tx_info, - } - ) + event["request"] = deepcopy(request_info) return event sentry_scope._name = LitestarIntegration.identifier diff --git a/tests/integrations/litestar/test_litestar.py b/tests/integrations/litestar/test_litestar.py index 057bd23dcb..db2d6724ec 100644 --- a/tests/integrations/litestar/test_litestar.py +++ b/tests/integrations/litestar/test_litestar.py @@ -138,6 +138,69 @@ def test_catch_exceptions( assert event["exception"]["values"][0]["mechanism"]["type"] == "litestar" +@pytest.mark.parametrize( + "test_url,expected_tx_name", + [ + ( + "/some_url", + "tests.integrations.litestar.test_litestar.litestar_app_factory..homepage_handler", + ), + ( + "/custom_error", + "custom_name", + ), + ( + "/controller/error", + "tests.integrations.litestar.test_litestar.litestar_app_factory..MyController.controller_error", + ), + ], +) +@pytest.mark.parametrize("span_streaming", [True, False]) +def test_transaction_name_and_source( + sentry_init, + capture_events, + test_url, + expected_tx_name, + capture_items, + span_streaming, +): + sentry_init( + traces_sample_rate=1.0, + integrations=[LitestarIntegration()], + _experiments={ + "trace_lifecycle": "stream" if span_streaming else "static", + }, + ) + litestar_app = litestar_app_factory() + client = TestClient(litestar_app) + + if span_streaming: + items = capture_items("span") + + try: + client.get(test_url) + except Exception: + pass + + sentry_sdk.flush() + spans = [item.payload for item in items] + + spans = [span for span in spans if span["name"] == expected_tx_name] + assert len(spans) == 1 + assert spans[0]["attributes"]["sentry.span.source"] == "component" + else: + events = capture_events() + + try: + client.get(test_url) + except Exception: + pass + + (_, transaction) = events + assert transaction["transaction"] == expected_tx_name + assert transaction["transaction_info"] == {"source": "component"} + + @pytest.mark.parametrize("span_streaming", [True, False]) def test_middleware_spans( sentry_init, From 35130d25c181a8ede2457c604d94a193b7392a87 Mon Sep 17 00:00:00 2001 From: Alexander Alderman Webb Date: Wed, 10 Jun 2026 13:08:19 +0200 Subject: [PATCH 2/2] . --- sentry_sdk/integrations/litestar.py | 2 +- tests/integrations/litestar/test_litestar.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/sentry_sdk/integrations/litestar.py b/sentry_sdk/integrations/litestar.py index e13ddf99d8..f0c90a7921 100644 --- a/sentry_sdk/integrations/litestar.py +++ b/sentry_sdk/integrations/litestar.py @@ -304,7 +304,7 @@ async def handle_wrapper( if not name: name = _DEFAULT_TRANSACTION_NAME - source = TransactionSource.ROUTE.value + source = TransactionSource.ROUTE sentry_sdk.set_transaction_name(name, source) sentry_scope.set_transaction_name(name, source) diff --git a/tests/integrations/litestar/test_litestar.py b/tests/integrations/litestar/test_litestar.py index db2d6724ec..4abb037e36 100644 --- a/tests/integrations/litestar/test_litestar.py +++ b/tests/integrations/litestar/test_litestar.py @@ -185,7 +185,7 @@ def test_transaction_name_and_source( sentry_sdk.flush() spans = [item.payload for item in items] - spans = [span for span in spans if span["name"] == expected_tx_name] + spans = [span for span in spans if expected_tx_name in span["name"]] assert len(spans) == 1 assert spans[0]["attributes"]["sentry.span.source"] == "component" else: @@ -197,7 +197,7 @@ def test_transaction_name_and_source( pass (_, transaction) = events - assert transaction["transaction"] == expected_tx_name + assert expected_tx_name in transaction["transaction"] assert transaction["transaction_info"] == {"source": "component"}