Skip to content

Commit 0d9595b

Browse files
feat(litestar): Set name and source on request span when streaming (#6553)
Call `Scope.set_transaction_name()` on isolation and current scopes instead of manually overwriting event fields in an event processor. This ensures that the span name and the `sentry.span.source` attribute are set on the segment span when the streaming trace lifecycle is enabled.
1 parent a6fdebd commit 0d9595b

2 files changed

Lines changed: 86 additions & 26 deletions

File tree

sentry_sdk/integrations/litestar.py

Lines changed: 23 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -287,40 +287,37 @@ async def handle_wrapper(
287287

288288
request_data = await body
289289

290-
def event_processor(event: "Event", _: "Hint") -> "Event":
291-
route_handler = scope.get("route_handler")
290+
route_handler = scope.get("route_handler")
291+
292+
func = None
293+
if route_handler.name is not None:
294+
name = route_handler.name
295+
# Accounts for use of type `Ref` in earlier versions of litestar without the need to reference it as a type
296+
elif hasattr(route_handler.fn, "value"):
297+
func = route_handler.fn.value
298+
else:
299+
func = route_handler.fn
300+
if func is not None:
301+
name = transaction_from_function(func)
302+
303+
source = SOURCE_FOR_STYLE["endpoint"]
292304

305+
if not name:
306+
name = _DEFAULT_TRANSACTION_NAME
307+
source = TransactionSource.ROUTE
308+
309+
sentry_sdk.set_transaction_name(name, source)
310+
sentry_scope.set_transaction_name(name, source)
311+
312+
def event_processor(event: "Event", _: "Hint") -> "Event":
293313
request_info = event.get("request", {})
294314
request_info["content_length"] = len(scope.get("_body", b""))
295315
if should_send_default_pii():
296316
request_info["cookies"] = extracted_request_data["cookies"]
297317
if request_data is not None:
298318
request_info["data"] = request_data
299319

300-
func = None
301-
if route_handler.name is not None:
302-
tx_name = route_handler.name
303-
# Accounts for use of type `Ref` in earlier versions of litestar without the need to reference it as a type
304-
elif hasattr(route_handler.fn, "value"):
305-
func = route_handler.fn.value
306-
else:
307-
func = route_handler.fn
308-
if func is not None:
309-
tx_name = transaction_from_function(func)
310-
311-
tx_info = {"source": SOURCE_FOR_STYLE["endpoint"]}
312-
313-
if not tx_name:
314-
tx_name = _DEFAULT_TRANSACTION_NAME
315-
tx_info = {"source": TransactionSource.ROUTE}
316-
317-
event.update(
318-
{
319-
"request": deepcopy(request_info),
320-
"transaction": tx_name,
321-
"transaction_info": tx_info,
322-
}
323-
)
320+
event["request"] = deepcopy(request_info)
324321
return event
325322

326323
sentry_scope._name = LitestarIntegration.identifier

tests/integrations/litestar/test_litestar.py

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,69 @@ def test_catch_exceptions(
138138
assert event["exception"]["values"][0]["mechanism"]["type"] == "litestar"
139139

140140

141+
@pytest.mark.parametrize(
142+
"test_url,expected_tx_name",
143+
[
144+
(
145+
"/some_url",
146+
"tests.integrations.litestar.test_litestar.litestar_app_factory.<locals>.homepage_handler",
147+
),
148+
(
149+
"/custom_error",
150+
"custom_name",
151+
),
152+
(
153+
"/controller/error",
154+
"tests.integrations.litestar.test_litestar.litestar_app_factory.<locals>.MyController.controller_error",
155+
),
156+
],
157+
)
158+
@pytest.mark.parametrize("span_streaming", [True, False])
159+
def test_transaction_name_and_source(
160+
sentry_init,
161+
capture_events,
162+
test_url,
163+
expected_tx_name,
164+
capture_items,
165+
span_streaming,
166+
):
167+
sentry_init(
168+
traces_sample_rate=1.0,
169+
integrations=[LitestarIntegration()],
170+
_experiments={
171+
"trace_lifecycle": "stream" if span_streaming else "static",
172+
},
173+
)
174+
litestar_app = litestar_app_factory()
175+
client = TestClient(litestar_app)
176+
177+
if span_streaming:
178+
items = capture_items("span")
179+
180+
try:
181+
client.get(test_url)
182+
except Exception:
183+
pass
184+
185+
sentry_sdk.flush()
186+
spans = [item.payload for item in items]
187+
188+
spans = [span for span in spans if expected_tx_name in span["name"]]
189+
assert len(spans) == 1
190+
assert spans[0]["attributes"]["sentry.span.source"] == "component"
191+
else:
192+
events = capture_events()
193+
194+
try:
195+
client.get(test_url)
196+
except Exception:
197+
pass
198+
199+
(_, transaction) = events
200+
assert expected_tx_name in transaction["transaction"]
201+
assert transaction["transaction_info"] == {"source": "component"}
202+
203+
141204
@pytest.mark.parametrize("span_streaming", [True, False])
142205
def test_middleware_spans(
143206
sentry_init,

0 commit comments

Comments
 (0)