1111from importlib .metadata import version
1212from typing import Any , Dict , Mapping , Optional
1313
14- from opentelemetry import trace
14+ from opentelemetry import propagate
15+ from opentelemetry import trace as trace_api
16+ from opentelemetry .baggage .propagation import W3CBaggagePropagator
1517from opentelemetry .exporter .otlp .proto .http .trace_exporter import OTLPSpanExporter
18+ from opentelemetry .propagators .composite import CompositePropagator
1619from opentelemetry .sdk .resources import Resource
17- from opentelemetry .sdk .trace import TracerProvider
20+ from opentelemetry .sdk .trace import TracerProvider as SDKTracerProvider
1821from opentelemetry .sdk .trace .export import BatchSpanProcessor , ConsoleSpanExporter , SimpleSpanProcessor
19- from opentelemetry .trace import StatusCode
22+ from opentelemetry .trace import Span , StatusCode
23+ from opentelemetry .trace .propagation .tracecontext import TraceContextTextMapPropagator
2024
2125from ..agent .agent_result import AgentResult
2226from ..types .content import Message , Messages
@@ -94,6 +98,7 @@ def __init__(
9498 otlp_endpoint : Optional [str ] = None ,
9599 otlp_headers : Optional [Dict [str , str ]] = None ,
96100 enable_console_export : Optional [bool ] = None ,
101+ tracer_provider : Optional [trace_api .TracerProvider ] = None ,
97102 ):
98103 """Initialize the tracer.
99104
@@ -102,6 +107,7 @@ def __init__(
102107 otlp_endpoint: OTLP endpoint URL for sending traces.
103108 otlp_headers: Headers to include with OTLP requests.
104109 enable_console_export: Whether to also export traces to console.
110+ tracer_provider: Optional existing TracerProvider to use instead of creating a new one.
105111 """
106112 # Check environment variables first
107113 env_endpoint = os .environ .get ("OTEL_EXPORTER_OTLP_ENDPOINT" )
@@ -133,16 +139,32 @@ def __init__(
133139
134140 self .service_name = service_name
135141 self .otlp_headers = otlp_headers or {}
136- self .tracer_provider : Optional [TracerProvider ] = None
137- self .tracer : Optional [trace .Tracer ] = None
138-
139- if self .otlp_endpoint or self .enable_console_export :
142+ self .tracer_provider = tracer_provider
143+ self .tracer : Optional [trace_api .Tracer ] = None
144+
145+ propagate .set_global_textmap (
146+ CompositePropagator (
147+ [
148+ W3CBaggagePropagator (),
149+ TraceContextTextMapPropagator (),
150+ ]
151+ )
152+ )
153+ if self .tracer_provider :
154+ # Use the provided tracer provider directly
155+ self .tracer = self .tracer_provider .get_tracer (self .service_name )
156+ elif self .otlp_endpoint or self .enable_console_export :
157+ # Create our own tracer provider
140158 self ._initialize_tracer ()
141159
142160 def _initialize_tracer (self ) -> None :
143161 """Initialize the OpenTelemetry tracer."""
144162 logger .info ("initializing tracer" )
145163
164+ # Use global tracer provider if already eixsts
165+ if self ._use_global_tracer_provider ():
166+ return
167+
146168 # Create resource with service information
147169 resource = Resource .create (
148170 {
@@ -154,7 +176,7 @@ def _initialize_tracer(self) -> None:
154176 )
155177
156178 # Create tracer provider
157- self .tracer_provider = TracerProvider (resource = resource )
179+ self .tracer_provider = SDKTracerProvider (resource = resource )
158180
159181 # Add console exporter if enabled
160182 if self .enable_console_export and self .tracer_provider :
@@ -190,15 +212,24 @@ def _initialize_tracer(self) -> None:
190212 logger .exception ("error=<%s> | Failed to configure OTLP exporter" , e )
191213
192214 # Set as global tracer provider
193- trace .set_tracer_provider (self .tracer_provider )
194- self .tracer = trace .get_tracer (self .service_name )
215+ trace_api .set_tracer_provider (self .tracer_provider )
216+ self .tracer = trace_api .get_tracer (self .service_name )
217+
218+ def _use_global_tracer_provider (self ) -> bool :
219+ current_provider = trace_api .get_tracer_provider ()
220+ if isinstance (current_provider , SDKTracerProvider ):
221+ logger .info ("using existing global tracer provider" )
222+ self .tracer_provider = current_provider
223+ self .tracer = current_provider .get_tracer (self .service_name )
224+ return True
225+ return False
195226
196227 def _start_span (
197228 self ,
198229 span_name : str ,
199- parent_span : Optional [trace . Span ] = None ,
230+ parent_span : Optional [Span ] = None ,
200231 attributes : Optional [Dict [str , AttributeValue ]] = None ,
201- ) -> Optional [trace . Span ]:
232+ ) -> Optional [Span ]:
202233 """Generic helper method to start a span with common attributes.
203234
204235 Args:
@@ -212,7 +243,7 @@ def _start_span(
212243 if self .tracer is None :
213244 return None
214245
215- context = trace .set_span_in_context (parent_span ) if parent_span else None
246+ context = trace_api .set_span_in_context (parent_span ) if parent_span else None
216247 span = self .tracer .start_span (name = span_name , context = context )
217248
218249 # Set start time as a common attribute
@@ -224,7 +255,7 @@ def _start_span(
224255
225256 return span
226257
227- def _set_attributes (self , span : trace . Span , attributes : Dict [str , AttributeValue ]) -> None :
258+ def _set_attributes (self , span : Span , attributes : Dict [str , AttributeValue ]) -> None :
228259 """Set attributes on a span, handling different value types appropriately.
229260
230261 Args:
@@ -239,7 +270,7 @@ def _set_attributes(self, span: trace.Span, attributes: Dict[str, AttributeValue
239270
240271 def _end_span (
241272 self ,
242- span : trace . Span ,
273+ span : Span ,
243274 attributes : Optional [Dict [str , AttributeValue ]] = None ,
244275 error : Optional [Exception ] = None ,
245276 ) -> None :
@@ -278,7 +309,7 @@ def _end_span(
278309 except Exception as e :
279310 logger .warning ("error=<%s> | failed to force flush tracer provider" , e )
280311
281- def end_span_with_error (self , span : trace . Span , error_message : str , exception : Optional [Exception ] = None ) -> None :
312+ def end_span_with_error (self , span : Span , error_message : str , exception : Optional [Exception ] = None ) -> None :
282313 """End a span with error status.
283314
284315 Args:
@@ -294,12 +325,12 @@ def end_span_with_error(self, span: trace.Span, error_message: str, exception: O
294325
295326 def start_model_invoke_span (
296327 self ,
297- parent_span : Optional [trace . Span ] = None ,
328+ parent_span : Optional [Span ] = None ,
298329 agent_name : str = "Strands Agent" ,
299330 messages : Optional [Messages ] = None ,
300331 model_id : Optional [str ] = None ,
301332 ** kwargs : Any ,
302- ) -> Optional [trace . Span ]:
333+ ) -> Optional [Span ]:
303334 """Start a new span for a model invocation.
304335
305336 Args:
@@ -328,7 +359,7 @@ def start_model_invoke_span(
328359 return self ._start_span ("Model invoke" , parent_span , attributes )
329360
330361 def end_model_invoke_span (
331- self , span : trace . Span , message : Message , usage : Usage , error : Optional [Exception ] = None
362+ self , span : Span , message : Message , usage : Usage , error : Optional [Exception ] = None
332363 ) -> None :
333364 """End a model invocation span with results and metrics.
334365
@@ -347,9 +378,7 @@ def end_model_invoke_span(
347378
348379 self ._end_span (span , attributes , error )
349380
350- def start_tool_call_span (
351- self , tool : ToolUse , parent_span : Optional [trace .Span ] = None , ** kwargs : Any
352- ) -> Optional [trace .Span ]:
381+ def start_tool_call_span (self , tool : ToolUse , parent_span : Optional [Span ] = None , ** kwargs : Any ) -> Optional [Span ]:
353382 """Start a new span for a tool call.
354383
355384 Args:
@@ -374,7 +403,7 @@ def start_tool_call_span(
374403 return self ._start_span (span_name , parent_span , attributes )
375404
376405 def end_tool_call_span (
377- self , span : trace . Span , tool_result : Optional [ToolResult ], error : Optional [Exception ] = None
406+ self , span : Span , tool_result : Optional [ToolResult ], error : Optional [Exception ] = None
378407 ) -> None :
379408 """End a tool call span with results.
380409
@@ -402,10 +431,10 @@ def end_tool_call_span(
402431 def start_event_loop_cycle_span (
403432 self ,
404433 event_loop_kwargs : Any ,
405- parent_span : Optional [trace . Span ] = None ,
434+ parent_span : Optional [Span ] = None ,
406435 messages : Optional [Messages ] = None ,
407436 ** kwargs : Any ,
408- ) -> Optional [trace . Span ]:
437+ ) -> Optional [Span ]:
409438 """Start a new span for an event loop cycle.
410439
411440 Args:
@@ -436,7 +465,7 @@ def start_event_loop_cycle_span(
436465
437466 def end_event_loop_cycle_span (
438467 self ,
439- span : trace . Span ,
468+ span : Span ,
440469 message : Message ,
441470 tool_result_message : Optional [Message ] = None ,
442471 error : Optional [Exception ] = None ,
@@ -466,7 +495,7 @@ def start_agent_span(
466495 tools : Optional [list ] = None ,
467496 custom_trace_attributes : Optional [Mapping [str , AttributeValue ]] = None ,
468497 ** kwargs : Any ,
469- ) -> Optional [trace . Span ]:
498+ ) -> Optional [Span ]:
470499 """Start a new span for an agent invocation.
471500
472501 Args:
@@ -506,7 +535,7 @@ def start_agent_span(
506535
507536 def end_agent_span (
508537 self ,
509- span : trace . Span ,
538+ span : Span ,
510539 response : Optional [AgentResult ] = None ,
511540 error : Optional [Exception ] = None ,
512541 ) -> None :
@@ -549,6 +578,7 @@ def get_tracer(
549578 otlp_endpoint : Optional [str ] = None ,
550579 otlp_headers : Optional [Dict [str , str ]] = None ,
551580 enable_console_export : Optional [bool ] = None ,
581+ tracer_provider : Optional [trace_api .TracerProvider ] = None ,
552582) -> Tracer :
553583 """Get or create the global tracer.
554584
@@ -557,18 +587,22 @@ def get_tracer(
557587 otlp_endpoint: OTLP endpoint URL for sending traces.
558588 otlp_headers: Headers to include with OTLP requests.
559589 enable_console_export: Whether to also export traces to console.
590+ tracer_provider: Optional existing TracerProvider to use instead of creating a new one.
560591
561592 Returns:
562593 The global tracer instance.
563594 """
564595 global _tracer_instance
565596
566- if _tracer_instance is None or (otlp_endpoint and _tracer_instance .otlp_endpoint != otlp_endpoint ): # type: ignore[unreachable]
597+ if (
598+ _tracer_instance is None or (otlp_endpoint and _tracer_instance .otlp_endpoint != otlp_endpoint ) # type: ignore[unreachable]
599+ ):
567600 _tracer_instance = Tracer (
568601 service_name = service_name ,
569602 otlp_endpoint = otlp_endpoint ,
570603 otlp_headers = otlp_headers ,
571604 enable_console_export = enable_console_export ,
605+ tracer_provider = tracer_provider ,
572606 )
573607
574608 return _tracer_instance
0 commit comments