Skip to content

Commit 069518d

Browse files
committed
fix(tracing): Restore scope transaction after streamed segment ends
1 parent 0f7acd5 commit 069518d

3 files changed

Lines changed: 93 additions & 0 deletions

File tree

‎sentry_sdk/scope.py‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -933,6 +933,8 @@ def streamed_span(self, span: "Optional[StreamedSpan]") -> None:
933933
self._transaction_info["source"] = str(
934934
span._attributes["sentry.segment.name.source"]
935935
)
936+
else:
937+
self._transaction_info.pop("source", None)
936938
return
937939

938940
if type(span) is NoOpStreamedSpan:
@@ -942,6 +944,8 @@ def streamed_span(self, span: "Optional[StreamedSpan]") -> None:
942944
self._transaction_info["source"] = str(
943945
span._attributes["sentry.segment.name.source"]
944946
)
947+
else:
948+
self._transaction_info.pop("source", None)
945949

946950
@property
947951
def profile(self) -> "Optional[Profile]":

‎sentry_sdk/traces.py‎

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,8 @@ class StreamedSpan:
254254
"_status",
255255
"_scope",
256256
"_previous_span_on_scope",
257+
"_previous_transaction_on_scope",
258+
"_previous_transaction_info_on_scope",
257259
"_baggage",
258260
"_sample_rand",
259261
"_sample_rate",
@@ -364,6 +366,11 @@ def finish(self, end_timestamp: "Optional[Union[float, datetime]]" = None) -> No
364366
def _start(self) -> None:
365367
if self._active:
366368
old_span = self._scope.streamed_span
369+
if self._is_segment():
370+
self._previous_transaction_on_scope = self._scope._transaction
371+
self._previous_transaction_info_on_scope = (
372+
self._scope._transaction_info.copy()
373+
)
367374
self._scope.streamed_span = self
368375
self._previous_span_on_scope = old_span
369376

@@ -383,6 +390,13 @@ def _end(self, end_timestamp: "Optional[Union[float, datetime]]" = None) -> None
383390
old_span = self._previous_span_on_scope
384391
del self._previous_span_on_scope
385392
self._scope.streamed_span = old_span
393+
if self._is_segment():
394+
self._scope._transaction = self._previous_transaction_on_scope
395+
del self._previous_transaction_on_scope
396+
self._scope._transaction_info = (
397+
self._previous_transaction_info_on_scope
398+
)
399+
del self._previous_transaction_info_on_scope
386400

387401
# Set attributes from the segment. These are set on span end on purpose
388402
# so that we have the best chance to capture the segment's final name
@@ -691,6 +705,11 @@ def _start(self) -> None:
691705
return
692706

693707
old_span = self._scope.streamed_span
708+
if self._is_segment():
709+
self._previous_transaction_on_scope = self._scope._transaction
710+
self._previous_transaction_info_on_scope = (
711+
self._scope._transaction_info.copy()
712+
)
694713
self._scope.streamed_span = self
695714
self._previous_span_on_scope = old_span
696715

@@ -715,6 +734,13 @@ def _end(self, end_timestamp: "Optional[Union[float, datetime]]" = None) -> None
715734
old_span = self._previous_span_on_scope
716735
del self._previous_span_on_scope
717736
self._scope.streamed_span = old_span
737+
if self._is_segment():
738+
self._scope._transaction = self._previous_transaction_on_scope
739+
del self._previous_transaction_on_scope
740+
self._scope._transaction_info = (
741+
self._previous_transaction_info_on_scope
742+
)
743+
del self._previous_transaction_info_on_scope
718744

719745
self._finished = True
720746

‎tests/tracing/test_integration_tests.py‎

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,69 @@ def test_error_event_linked_without_performance_span_streaming(
110110
assert event["transaction_info"] == {"source": "custom segment source"}
111111

112112

113+
@pytest.mark.parametrize("sample_rate", [0.0, 1.0])
114+
def test_span_streaming_clears_transaction_name(
115+
sentry_init, capture_events, sample_rate
116+
):
117+
sentry_init(traces_sample_rate=sample_rate, trace_lifecycle="stream")
118+
events = capture_events()
119+
scope = sentry_sdk.get_current_scope()
120+
121+
with sentry_sdk.traces.start_span(
122+
name="streamed segment",
123+
attributes={"sentry.segment.name.source": "component"},
124+
):
125+
assert scope._transaction == "streamed segment"
126+
assert scope._transaction_info == {"source": "component"}
127+
128+
assert scope._transaction is None
129+
assert scope._transaction_info == {}
130+
131+
sentry_sdk.capture_message("after streamed segment")
132+
133+
(event,) = events
134+
assert "transaction" not in event
135+
136+
137+
@pytest.mark.parametrize("sample_rate", [0.0, 1.0])
138+
def test_span_streaming_restores_transaction_name(
139+
sentry_init, capture_events, sample_rate
140+
):
141+
sentry_init(traces_sample_rate=sample_rate, trace_lifecycle="stream")
142+
events = capture_events()
143+
scope = sentry_sdk.get_current_scope()
144+
scope.set_transaction_name("previous transaction", source="route")
145+
146+
with sentry_sdk.traces.start_span(name="streamed segment"):
147+
assert scope._transaction == "streamed segment"
148+
assert scope._transaction_info == ({"source": "custom"} if sample_rate else {})
149+
150+
assert scope._transaction == "previous transaction"
151+
assert scope._transaction_info == {"source": "route"}
152+
153+
sentry_sdk.capture_message("after streamed segment")
154+
155+
(event,) = events
156+
assert event["transaction"] == "previous transaction"
157+
assert event["transaction_info"] == {"source": "route"}
158+
159+
160+
@pytest.mark.parametrize("sample_rate", [0.0, 1.0])
161+
def test_span_streaming_keeps_name_changed_during_child_span(sentry_init, sample_rate):
162+
sentry_init(traces_sample_rate=sample_rate, trace_lifecycle="stream")
163+
scope = sentry_sdk.get_current_scope()
164+
165+
with sentry_sdk.traces.start_span(name="streamed segment"):
166+
with sentry_sdk.traces.start_span(name="child"):
167+
scope.set_transaction_name("renamed segment", source="component")
168+
169+
assert scope._transaction == "renamed segment"
170+
assert scope._transaction_info == {"source": "component"}
171+
172+
assert scope._transaction is None
173+
assert scope._transaction_info == {}
174+
175+
113176
@pytest.mark.parametrize("parent_sampled", [True, False, None])
114177
@pytest.mark.parametrize("sample_rate", [0.0, 1.0])
115178
def test_continue_trace(sentry_init, capture_envelopes, parent_sampled, sample_rate):

0 commit comments

Comments
 (0)