Skip to content

Commit b6107ba

Browse files
committed
fix(starlette): Set transaction name on current scope in sync handler
The sync request/response handler passed the _isolation_ scope to `_set_transaction_name_and_source`, but the transaction/segment span lives on the _current_ scope. As a result the route-resolved name never reached the span for sync endpoints, which were instead named by the raw URL from the ASGI middleware (`transaction_info.source` of `url` rather than `route`). Async handlers already used the current scope and were unaffected. Pass the current scope (already computed above) so sync and async handlers behave identically: - streaming: the segment name / `sentry.segment.name.source` are route-based - static: the transaction event name / source are route-based For parametrized routes this also removes high-cardinality URL transaction names for sync endpoints.
1 parent 1c3b50d commit b6107ba

2 files changed

Lines changed: 42 additions & 1 deletion

File tree

sentry_sdk/integrations/starlette.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -632,7 +632,7 @@ def _sentry_sync_func(*args: "Any", **kwargs: "Any") -> "Any":
632632
request = args[0]
633633

634634
_set_transaction_name_and_source(
635-
sentry_scope, integration.transaction_style, request
635+
current_scope, integration.transaction_style, request
636636
)
637637

638638
extractor = StarletteRequestExtractor(request)

tests/integrations/starlette/test_starlette.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1499,6 +1499,47 @@ def test_active_thread_id_span_streaming(sentry_init, capture_items, endpoint):
14991499
assert str(data["active"]) == segments[0]["attributes"]["thread.id"]
15001500

15011501

1502+
@pytest.mark.parametrize("endpoint", ["/sync/thread_ids", "/async/thread_ids"])
1503+
def test_transaction_name_span_streaming(sentry_init, capture_items, endpoint):
1504+
sentry_init(
1505+
auto_enabling_integrations=False,
1506+
integrations=[StarletteIntegration(transaction_style="url")],
1507+
traces_sample_rate=1.0,
1508+
trace_lifecycle="stream",
1509+
)
1510+
app = starlette_app_factory()
1511+
1512+
items = capture_items("span")
1513+
1514+
client = TestClient(app)
1515+
response = client.get(endpoint)
1516+
assert response.status_code == 200
1517+
1518+
sentry_sdk.flush()
1519+
1520+
segments = [item.payload for item in items if item.payload.get("is_segment")]
1521+
assert len(segments) == 1
1522+
assert segments[0]["name"] == endpoint
1523+
assert segments[0]["attributes"]["sentry.segment.name.source"] == "route"
1524+
1525+
1526+
@pytest.mark.parametrize("endpoint", ["/sync/thread_ids", "/async/thread_ids"])
1527+
def test_transaction_name_static(sentry_init, capture_events, endpoint):
1528+
sentry_init(
1529+
integrations=[StarletteIntegration(transaction_style="url")],
1530+
traces_sample_rate=1.0,
1531+
)
1532+
events = capture_events()
1533+
1534+
client = TestClient(starlette_app_factory())
1535+
response = client.get(endpoint)
1536+
assert response.status_code == 200
1537+
1538+
(transaction,) = [e for e in events if e.get("type") == "transaction"]
1539+
assert transaction["transaction"] == endpoint
1540+
assert transaction["transaction_info"] == {"source": "route"}
1541+
1542+
15021543
def test_original_request_not_scrubbed(sentry_init, capture_events):
15031544
sentry_init(integrations=[StarletteIntegration()])
15041545

0 commit comments

Comments
 (0)