1+ from sentry_sdk import get_client
12from sentry_sdk .integrations import Integration , DidNotEnable
23from sentry_sdk .scope import register_external_propagation_context
34from sentry_sdk .utils import logger , Dsn
45from sentry_sdk .consts import VERSION , EndpointType
6+ from sentry_sdk .tracing_utils import Baggage
7+ from sentry_sdk .tracing import (
8+ BAGGAGE_HEADER_NAME ,
9+ SENTRY_TRACE_HEADER_NAME ,
10+ )
511
612try :
7- from opentelemetry import trace
813 from opentelemetry .propagate import set_global_textmap
914 from opentelemetry .sdk .trace import TracerProvider
1015 from opentelemetry .sdk .trace .export import BatchSpanProcessor
1116 from opentelemetry .exporter .otlp .proto .http .trace_exporter import OTLPSpanExporter
1217
18+ from opentelemetry .trace import (
19+ get_current_span ,
20+ get_tracer_provider ,
21+ set_tracer_provider ,
22+ format_trace_id ,
23+ format_span_id ,
24+ SpanContext ,
25+ INVALID_SPAN_ID ,
26+ INVALID_TRACE_ID ,
27+ )
28+
29+ from opentelemetry .context import (
30+ Context ,
31+ get_current ,
32+ get_value ,
33+ )
34+
35+ from opentelemetry .propagators .textmap import (
36+ CarrierT ,
37+ Setter ,
38+ default_setter ,
39+ )
40+
1341 from sentry_sdk .integrations .opentelemetry .propagator import SentryPropagator
42+ from sentry_sdk .integrations .opentelemetry .consts import SENTRY_BAGGAGE_KEY
1443except ImportError :
1544 raise DidNotEnable ("opentelemetry-distro[otlp] is not installed" )
1645
@@ -25,22 +54,22 @@ def otel_propagation_context():
2554 """
2655 Get the (trace_id, span_id) from opentelemetry if exists.
2756 """
28- ctx = trace . get_current_span ().get_span_context ()
57+ ctx = get_current_span ().get_span_context ()
2958
30- if ctx .trace_id == trace . INVALID_TRACE_ID or ctx .span_id == trace . INVALID_SPAN_ID :
59+ if ctx .trace_id == INVALID_TRACE_ID or ctx .span_id == INVALID_SPAN_ID :
3160 return None
3261
33- return (trace . format_trace_id (ctx .trace_id ), trace . format_span_id (ctx .span_id ))
62+ return (format_trace_id (ctx .trace_id ), format_span_id (ctx .span_id ))
3463
3564
3665def setup_otlp_traces_exporter (dsn = None ):
3766 # type: (Optional[str]) -> None
38- tracer_provider = trace . get_tracer_provider ()
67+ tracer_provider = get_tracer_provider ()
3968
4069 if not isinstance (tracer_provider , TracerProvider ):
4170 logger .debug ("[OTLP] No TracerProvider configured by user, creating a new one" )
4271 tracer_provider = TracerProvider ()
43- trace . set_tracer_provider (tracer_provider )
72+ set_tracer_provider (tracer_provider )
4473
4574 endpoint = None
4675 headers = None
@@ -55,6 +84,54 @@ def setup_otlp_traces_exporter(dsn=None):
5584 tracer_provider .add_span_processor (span_processor )
5685
5786
87+ class SentryOTLPPropagator (SentryPropagator ):
88+ """
89+ We need to override the inject of the older propagator since that
90+ is SpanProcessor based.
91+
92+ !!! Note regarding baggage:
93+ We cannot meaningfully populate a new baggage as a head SDK
94+ when we are using OTLP since we don't have any sort of transaction semantic to
95+ track state across a group of spans.
96+
97+ For incoming baggage, we just pass it on as is so that case is correctly handled.
98+ """
99+
100+ def inject (self , carrier , context = None , setter = default_setter ):
101+ # type: (CarrierT, Optional[Context], Setter[CarrierT]) -> None
102+ otlp_integration = get_client ().get_integration (OTLPIntegration )
103+ if otlp_integration is None :
104+ return
105+
106+ if context is None :
107+ context = get_current ()
108+
109+ current_span = get_current_span (context )
110+ current_span_context = current_span .get_span_context ()
111+
112+ if not current_span_context .is_valid :
113+ return
114+
115+ sentry_trace = _to_traceparent (current_span_context )
116+ setter .set (carrier , SENTRY_TRACE_HEADER_NAME , sentry_trace )
117+
118+ baggage = get_value (SENTRY_BAGGAGE_KEY , context )
119+ if baggage is not None and isinstance (baggage , Baggage ):
120+ setter .set (carrier , BAGGAGE_HEADER_NAME , baggage .serialize ())
121+
122+
123+ def _to_traceparent (span_context ):
124+ # type: (SpanContext) -> str
125+ """
126+ Helper method to generate the sentry-trace header.
127+ """
128+ span_id = format_span_id (span_context .span_id )
129+ trace_id = format_trace_id (span_context .trace_id )
130+ sampled = span_context .trace_flags .sampled
131+
132+ return f"{ trace_id } -{ span_id } -{ '1' if sampled else '0' } "
133+
134+
58135class OTLPIntegration (Integration ):
59136 identifier = "otlp"
60137
@@ -79,4 +156,4 @@ def setup_once_with_options(self, options=None):
79156 if self .setup_propagator :
80157 logger .debug ("[OTLP] Setting up propagator for distributed tracing" )
81158 # TODO-neel better propagator support, chain with existing ones if possible instead of replacing
82- set_global_textmap (SentryPropagator ())
159+ set_global_textmap (SentryOTLPPropagator ())
0 commit comments