diff --git a/sentry_sdk/scope.py b/sentry_sdk/scope.py index 6954a46915..1aa9f33e15 100644 --- a/sentry_sdk/scope.py +++ b/sentry_sdk/scope.py @@ -933,8 +933,13 @@ def streamed_span(self, span: "Optional[StreamedSpan]") -> None: ) return - if type(span) is NoOpStreamedSpan and span._name is not None: - self._transaction = span.name + if type(span) is NoOpStreamedSpan: + if span._name is not None: + self._transaction = span.name + if span._attributes.get("sentry.segment.name.source"): + self._transaction_info["source"] = str( + span._attributes["sentry.segment.name.source"] + ) @property def profile(self) -> "Optional[Profile]": @@ -1314,6 +1319,7 @@ def start_streamed_span( if is_ignored_span(name, attributes): return NoOpStreamedSpan( name=name, + attributes=attributes, scope=self, segment=None, trace_id=propagation_context.trace_id, @@ -1335,6 +1341,7 @@ def start_streamed_span( if sampled is False or sampled is None: return NoOpStreamedSpan( name=name, + attributes=attributes, scope=self, segment=None, trace_id=propagation_context.trace_id, @@ -1366,6 +1373,7 @@ def start_streamed_span( if is_ignored_span(name, attributes): return NoOpStreamedSpan( name=name, + attributes=attributes, segment=parent_span._segment, trace_id=parent_span.trace_id, parent_span_id=parent_span.span_id, @@ -1376,6 +1384,7 @@ def start_streamed_span( if isinstance(parent_span, NoOpStreamedSpan): return NoOpStreamedSpan( name=name, + attributes=attributes, segment=parent_span._segment, trace_id=parent_span.trace_id, parent_span_id=parent_span.span_id, diff --git a/sentry_sdk/traces.py b/sentry_sdk/traces.py index 904a40bbc4..b02167672d 100644 --- a/sentry_sdk/traces.py +++ b/sentry_sdk/traces.py @@ -633,6 +633,7 @@ class NoOpStreamedSpan(StreamedSpan): def __init__( self, name: "Optional[str]" = None, + attributes: "Optional[Attributes]" = None, segment: "Optional[StreamedSpan]" = None, trace_id: "Optional[str]" = None, parent_span_id: "Optional[str]" = None, @@ -645,6 +646,11 @@ def __init__( sample_rate: "Optional[float]" = None, ) -> None: self._name = name # type: ignore[assignment] + self._attributes = {} + if attributes is not None and "sentry.segment.name.source" in attributes: + self.set_attribute( + "sentry.segment.name.source", attributes["sentry.segment.name.source"] + ) self._span_id: "Optional[str]" = None @@ -724,10 +730,17 @@ def get_attributes(self) -> "Attributes": return {} def set_attribute(self, key: str, value: "AttributeValue") -> None: - pass + if key != "sentry.segment.name.source": + return + + super().set_attribute("sentry.segment.name.source", value) def set_attributes(self, attributes: "Attributes") -> None: - pass + for key, value in attributes.items(): + if key != "sentry.segment.name.source": + continue + + self.set_attribute("sentry.segment.name.source", value) def remove_attribute(self, key: str) -> None: pass diff --git a/tests/tracing/test_integration_tests.py b/tests/tracing/test_integration_tests.py index b293d36134..59bb1f9d02 100644 --- a/tests/tracing/test_integration_tests.py +++ b/tests/tracing/test_integration_tests.py @@ -97,13 +97,17 @@ def test_error_event_linked_without_performance_span_streaming( sentry_init(traces_sample_rate=None, trace_lifecycle="stream") items = capture_items("event") - with sentry_sdk.traces.start_span(name="no-op span"): + with sentry_sdk.traces.start_span( + name="no-op span", + attributes={"sentry.segment.name.source": "custom segment source"}, + ): sentry_sdk.capture_message("hi") sentry_sdk.flush() (event,) = (item.payload for item in items) assert event["transaction"] == "no-op span" + assert event["transaction_info"] == {"source": "custom segment source"} @pytest.mark.parametrize("parent_sampled", [True, False, None])