Skip to content

Commit 0ebb2f9

Browse files
fix: fix propagation of OTEL NonRecordingSpan (#2187)
1 parent d08ebdb commit 0ebb2f9

File tree

4 files changed

+77
-45
lines changed

4 files changed

+77
-45
lines changed

sentry_sdk/integrations/opentelemetry/propagator.py

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@
1313
default_setter,
1414
)
1515
from opentelemetry.trace import ( # type: ignore
16-
TraceFlags,
1716
NonRecordingSpan,
1817
SpanContext,
18+
TraceFlags,
1919
)
2020
from sentry_sdk.integrations.opentelemetry.consts import (
2121
SENTRY_BAGGAGE_KEY,
@@ -90,11 +90,12 @@ def inject(self, carrier, context=None, setter=default_setter):
9090
context = get_current()
9191

9292
current_span = trace.get_current_span(context)
93+
current_span_context = current_span.get_span_context()
9394

94-
if not current_span.context.is_valid:
95+
if not current_span_context.is_valid:
9596
return
9697

97-
span_id = trace.format_span_id(current_span.context.span_id)
98+
span_id = trace.format_span_id(current_span_context.span_id)
9899

99100
span_map = SentrySpanProcessor().otel_span_map
100101
sentry_span = span_map.get(span_id, None)
@@ -103,9 +104,10 @@ def inject(self, carrier, context=None, setter=default_setter):
103104

104105
setter.set(carrier, SENTRY_TRACE_HEADER_NAME, sentry_span.to_traceparent())
105106

106-
baggage = sentry_span.containing_transaction.get_baggage()
107-
if baggage:
108-
setter.set(carrier, BAGGAGE_HEADER_NAME, baggage.serialize())
107+
if sentry_span.containing_transaction:
108+
baggage = sentry_span.containing_transaction.get_baggage()
109+
if baggage:
110+
setter.set(carrier, BAGGAGE_HEADER_NAME, baggage.serialize())
109111

110112
@property
111113
def fields(self):

sentry_sdk/integrations/opentelemetry/span_processor.py

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -29,16 +29,15 @@
2929
from urllib3.util import parse_url as urlparse
3030

3131
if TYPE_CHECKING:
32-
from typing import Any
33-
from typing import Dict
34-
from typing import Union
32+
from typing import Any, Dict, Optional, Union
33+
3534
from sentry_sdk._types import Event, Hint
3635

3736
OPEN_TELEMETRY_CONTEXT = "otel"
3837

3938

4039
def link_trace_context_to_error_event(event, otel_span_map):
41-
# type: (Event, Dict[str, Union[Transaction, OTelSpan]]) -> Event
40+
# type: (Event, Dict[str, Union[Transaction, SentrySpan]]) -> Event
4241
hub = Hub.current
4342
if not hub:
4443
return event
@@ -76,7 +75,7 @@ class SentrySpanProcessor(SpanProcessor): # type: ignore
7675
"""
7776

7877
# The mapping from otel span ids to sentry spans
79-
otel_span_map = {} # type: Dict[str, Union[Transaction, OTelSpan]]
78+
otel_span_map = {} # type: Dict[str, Union[Transaction, SentrySpan]]
8079

8180
def __new__(cls):
8281
# type: () -> SentrySpanProcessor
@@ -93,7 +92,7 @@ def global_event_processor(event, hint):
9392
return link_trace_context_to_error_event(event, self.otel_span_map)
9493

9594
def on_start(self, otel_span, parent_context=None):
96-
# type: (OTelSpan, SpanContext) -> None
95+
# type: (OTelSpan, Optional[SpanContext]) -> None
9796
hub = Hub.current
9897
if not hub:
9998
return
@@ -109,7 +108,7 @@ def on_start(self, otel_span, parent_context=None):
109108
if hub.client and hub.client.options["instrumenter"] != INSTRUMENTER.OTEL:
110109
return
111110

112-
if not otel_span.context.is_valid:
111+
if not otel_span.get_span_context().is_valid:
113112
return
114113

115114
if self._is_sentry_span(hub, otel_span):
@@ -152,10 +151,11 @@ def on_end(self, otel_span):
152151
if hub.client and hub.client.options["instrumenter"] != INSTRUMENTER.OTEL:
153152
return
154153

155-
if not otel_span.context.is_valid:
154+
span_context = otel_span.get_span_context()
155+
if not span_context.is_valid:
156156
return
157157

158-
span_id = format_span_id(otel_span.context.span_id)
158+
span_id = format_span_id(span_context.span_id)
159159
sentry_span = self.otel_span_map.pop(span_id, None)
160160
if not sentry_span:
161161
return
@@ -211,11 +211,12 @@ def _get_trace_data(self, otel_span, parent_context):
211211
Extracts tracing information from one OTel span and its parent OTel context.
212212
"""
213213
trace_data = {}
214+
span_context = otel_span.get_span_context()
214215

215-
span_id = format_span_id(otel_span.context.span_id)
216+
span_id = format_span_id(span_context.span_id)
216217
trace_data["span_id"] = span_id
217218

218-
trace_id = format_trace_id(otel_span.context.trace_id)
219+
trace_id = format_trace_id(span_context.trace_id)
219220
trace_data["trace_id"] = trace_id
220221

221222
parent_span_id = (

tests/integrations/opentelemetry/test_propagator.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ def test_inject_empty_otel_span_map():
139139
is_remote=True,
140140
)
141141
span = MagicMock()
142-
span.context = span_context
142+
span.get_span_context.return_value = span_context
143143

144144
with mock.patch(
145145
"sentry_sdk.integrations.opentelemetry.propagator.trace.get_current_span",
@@ -170,7 +170,7 @@ def test_inject_sentry_span_no_baggage():
170170
is_remote=True,
171171
)
172172
span = MagicMock()
173-
span.context = span_context
173+
span.get_span_context.return_value = span_context
174174

175175
sentry_span = MagicMock()
176176
sentry_span.to_traceparent = mock.Mock(
@@ -214,7 +214,7 @@ def test_inject_sentry_span_baggage():
214214
is_remote=True,
215215
)
216216
span = MagicMock()
217-
span.context = span_context
217+
span.get_span_context.return_value = span_context
218218

219219
sentry_span = MagicMock()
220220
sentry_span.to_traceparent = mock.Mock(

tests/integrations/opentelemetry/test_span_processor.py

Lines changed: 54 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -62,9 +62,12 @@ def test_get_otel_context():
6262

6363
def test_get_trace_data_with_span_and_trace():
6464
otel_span = MagicMock()
65-
otel_span.context = MagicMock()
66-
otel_span.context.trace_id = int("1234567890abcdef1234567890abcdef", 16)
67-
otel_span.context.span_id = int("1234567890abcdef", 16)
65+
span_context = SpanContext(
66+
trace_id=int("1234567890abcdef1234567890abcdef", 16),
67+
span_id=int("1234567890abcdef", 16),
68+
is_remote=True,
69+
)
70+
otel_span.get_span_context.return_value = span_context
6871
otel_span.parent = None
6972

7073
parent_context = {}
@@ -80,9 +83,12 @@ def test_get_trace_data_with_span_and_trace():
8083

8184
def test_get_trace_data_with_span_and_trace_and_parent():
8285
otel_span = MagicMock()
83-
otel_span.context = MagicMock()
84-
otel_span.context.trace_id = int("1234567890abcdef1234567890abcdef", 16)
85-
otel_span.context.span_id = int("1234567890abcdef", 16)
86+
span_context = SpanContext(
87+
trace_id=int("1234567890abcdef1234567890abcdef", 16),
88+
span_id=int("1234567890abcdef", 16),
89+
is_remote=True,
90+
)
91+
otel_span.get_span_context.return_value = span_context
8692
otel_span.parent = MagicMock()
8793
otel_span.parent.span_id = int("abcdef1234567890", 16)
8894

@@ -99,9 +105,12 @@ def test_get_trace_data_with_span_and_trace_and_parent():
99105

100106
def test_get_trace_data_with_sentry_trace():
101107
otel_span = MagicMock()
102-
otel_span.context = MagicMock()
103-
otel_span.context.trace_id = int("1234567890abcdef1234567890abcdef", 16)
104-
otel_span.context.span_id = int("1234567890abcdef", 16)
108+
span_context = SpanContext(
109+
trace_id=int("1234567890abcdef1234567890abcdef", 16),
110+
span_id=int("1234567890abcdef", 16),
111+
is_remote=True,
112+
)
113+
otel_span.get_span_context.return_value = span_context
105114
otel_span.parent = MagicMock()
106115
otel_span.parent.span_id = int("abcdef1234567890", 16)
107116

@@ -144,9 +153,12 @@ def test_get_trace_data_with_sentry_trace():
144153

145154
def test_get_trace_data_with_sentry_trace_and_baggage():
146155
otel_span = MagicMock()
147-
otel_span.context = MagicMock()
148-
otel_span.context.trace_id = int("1234567890abcdef1234567890abcdef", 16)
149-
otel_span.context.span_id = int("1234567890abcdef", 16)
156+
span_context = SpanContext(
157+
trace_id=int("1234567890abcdef1234567890abcdef", 16),
158+
span_id=int("1234567890abcdef", 16),
159+
is_remote=True,
160+
)
161+
otel_span.get_span_context.return_value = span_context
150162
otel_span.parent = MagicMock()
151163
otel_span.parent.span_id = int("abcdef1234567890", 16)
152164

@@ -263,9 +275,12 @@ def test_on_start_transaction():
263275
otel_span = MagicMock()
264276
otel_span.name = "Sample OTel Span"
265277
otel_span.start_time = time.time_ns()
266-
otel_span.context = MagicMock()
267-
otel_span.context.trace_id = int("1234567890abcdef1234567890abcdef", 16)
268-
otel_span.context.span_id = int("1234567890abcdef", 16)
278+
span_context = SpanContext(
279+
trace_id=int("1234567890abcdef1234567890abcdef", 16),
280+
span_id=int("1234567890abcdef", 16),
281+
is_remote=True,
282+
)
283+
otel_span.get_span_context.return_value = span_context
269284
otel_span.parent = MagicMock()
270285
otel_span.parent.span_id = int("abcdef1234567890", 16)
271286

@@ -305,9 +320,12 @@ def test_on_start_child():
305320
otel_span = MagicMock()
306321
otel_span.name = "Sample OTel Span"
307322
otel_span.start_time = time.time_ns()
308-
otel_span.context = MagicMock()
309-
otel_span.context.trace_id = int("1234567890abcdef1234567890abcdef", 16)
310-
otel_span.context.span_id = int("1234567890abcdef", 16)
323+
span_context = SpanContext(
324+
trace_id=int("1234567890abcdef1234567890abcdef", 16),
325+
span_id=int("1234567890abcdef", 16),
326+
is_remote=True,
327+
)
328+
otel_span.get_span_context.return_value = span_context
311329
otel_span.parent = MagicMock()
312330
otel_span.parent.span_id = int("abcdef1234567890", 16)
313331

@@ -351,8 +369,12 @@ def test_on_end_no_sentry_span():
351369
otel_span = MagicMock()
352370
otel_span.name = "Sample OTel Span"
353371
otel_span.end_time = time.time_ns()
354-
otel_span.context = MagicMock()
355-
otel_span.context.span_id = int("1234567890abcdef", 16)
372+
span_context = SpanContext(
373+
trace_id=int("1234567890abcdef1234567890abcdef", 16),
374+
span_id=int("1234567890abcdef", 16),
375+
is_remote=True,
376+
)
377+
otel_span.get_span_context.return_value = span_context
356378

357379
span_processor = SentrySpanProcessor()
358380
span_processor.otel_span_map = {}
@@ -372,8 +394,12 @@ def test_on_end_sentry_transaction():
372394
otel_span = MagicMock()
373395
otel_span.name = "Sample OTel Span"
374396
otel_span.end_time = time.time_ns()
375-
otel_span.context = MagicMock()
376-
otel_span.context.span_id = int("1234567890abcdef", 16)
397+
span_context = SpanContext(
398+
trace_id=int("1234567890abcdef1234567890abcdef", 16),
399+
span_id=int("1234567890abcdef", 16),
400+
is_remote=True,
401+
)
402+
otel_span.get_span_context.return_value = span_context
377403

378404
fake_sentry_span = MagicMock(spec=Transaction)
379405
fake_sentry_span.set_context = MagicMock()
@@ -398,8 +424,12 @@ def test_on_end_sentry_span():
398424
otel_span = MagicMock()
399425
otel_span.name = "Sample OTel Span"
400426
otel_span.end_time = time.time_ns()
401-
otel_span.context = MagicMock()
402-
otel_span.context.span_id = int("1234567890abcdef", 16)
427+
span_context = SpanContext(
428+
trace_id=int("1234567890abcdef1234567890abcdef", 16),
429+
span_id=int("1234567890abcdef", 16),
430+
is_remote=True,
431+
)
432+
otel_span.get_span_context.return_value = span_context
403433

404434
fake_sentry_span = MagicMock(spec=Span)
405435
fake_sentry_span.set_context = MagicMock()
@@ -425,7 +455,6 @@ def test_link_trace_context_to_error_event():
425455
"""
426456
fake_client = MagicMock()
427457
fake_client.options = {"instrumenter": "otel"}
428-
fake_client
429458

430459
current_hub = MagicMock()
431460
current_hub.client = fake_client

0 commit comments

Comments
 (0)