11
11
from importlib .metadata import version
12
12
from typing import Any , Dict , Mapping , Optional
13
13
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
15
17
from opentelemetry .exporter .otlp .proto .http .trace_exporter import OTLPSpanExporter
18
+ from opentelemetry .propagators .composite import CompositePropagator
16
19
from opentelemetry .sdk .resources import Resource
17
- from opentelemetry .sdk .trace import TracerProvider
20
+ from opentelemetry .sdk .trace import TracerProvider as SDKTracerProvider
18
21
from 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
20
24
21
25
from ..agent .agent_result import AgentResult
22
26
from ..types .content import Message , Messages
@@ -94,6 +98,7 @@ def __init__(
94
98
otlp_endpoint : Optional [str ] = None ,
95
99
otlp_headers : Optional [Dict [str , str ]] = None ,
96
100
enable_console_export : Optional [bool ] = None ,
101
+ tracer_provider : Optional [trace_api .TracerProvider ] = None ,
97
102
):
98
103
"""Initialize the tracer.
99
104
@@ -102,6 +107,7 @@ def __init__(
102
107
otlp_endpoint: OTLP endpoint URL for sending traces.
103
108
otlp_headers: Headers to include with OTLP requests.
104
109
enable_console_export: Whether to also export traces to console.
110
+ tracer_provider: Optional existing TracerProvider to use instead of creating a new one.
105
111
"""
106
112
# Check environment variables first
107
113
env_endpoint = os .environ .get ("OTEL_EXPORTER_OTLP_ENDPOINT" )
@@ -133,16 +139,32 @@ def __init__(
133
139
134
140
self .service_name = service_name
135
141
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
140
158
self ._initialize_tracer ()
141
159
142
160
def _initialize_tracer (self ) -> None :
143
161
"""Initialize the OpenTelemetry tracer."""
144
162
logger .info ("initializing tracer" )
145
163
164
+ # Use global tracer provider if already eixsts
165
+ if self ._use_global_tracer_provider ():
166
+ return
167
+
146
168
# Create resource with service information
147
169
resource = Resource .create (
148
170
{
@@ -154,7 +176,7 @@ def _initialize_tracer(self) -> None:
154
176
)
155
177
156
178
# Create tracer provider
157
- self .tracer_provider = TracerProvider (resource = resource )
179
+ self .tracer_provider = SDKTracerProvider (resource = resource )
158
180
159
181
# Add console exporter if enabled
160
182
if self .enable_console_export and self .tracer_provider :
@@ -190,15 +212,24 @@ def _initialize_tracer(self) -> None:
190
212
logger .exception ("error=<%s> | Failed to configure OTLP exporter" , e )
191
213
192
214
# 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
195
226
196
227
def _start_span (
197
228
self ,
198
229
span_name : str ,
199
- parent_span : Optional [trace . Span ] = None ,
230
+ parent_span : Optional [Span ] = None ,
200
231
attributes : Optional [Dict [str , AttributeValue ]] = None ,
201
- ) -> Optional [trace . Span ]:
232
+ ) -> Optional [Span ]:
202
233
"""Generic helper method to start a span with common attributes.
203
234
204
235
Args:
@@ -212,7 +243,7 @@ def _start_span(
212
243
if self .tracer is None :
213
244
return None
214
245
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
216
247
span = self .tracer .start_span (name = span_name , context = context )
217
248
218
249
# Set start time as a common attribute
@@ -224,7 +255,7 @@ def _start_span(
224
255
225
256
return span
226
257
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 :
228
259
"""Set attributes on a span, handling different value types appropriately.
229
260
230
261
Args:
@@ -239,7 +270,7 @@ def _set_attributes(self, span: trace.Span, attributes: Dict[str, AttributeValue
239
270
240
271
def _end_span (
241
272
self ,
242
- span : trace . Span ,
273
+ span : Span ,
243
274
attributes : Optional [Dict [str , AttributeValue ]] = None ,
244
275
error : Optional [Exception ] = None ,
245
276
) -> None :
@@ -278,7 +309,7 @@ def _end_span(
278
309
except Exception as e :
279
310
logger .warning ("error=<%s> | failed to force flush tracer provider" , e )
280
311
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 :
282
313
"""End a span with error status.
283
314
284
315
Args:
@@ -294,12 +325,12 @@ def end_span_with_error(self, span: trace.Span, error_message: str, exception: O
294
325
295
326
def start_model_invoke_span (
296
327
self ,
297
- parent_span : Optional [trace . Span ] = None ,
328
+ parent_span : Optional [Span ] = None ,
298
329
agent_name : str = "Strands Agent" ,
299
330
messages : Optional [Messages ] = None ,
300
331
model_id : Optional [str ] = None ,
301
332
** kwargs : Any ,
302
- ) -> Optional [trace . Span ]:
333
+ ) -> Optional [Span ]:
303
334
"""Start a new span for a model invocation.
304
335
305
336
Args:
@@ -328,7 +359,7 @@ def start_model_invoke_span(
328
359
return self ._start_span ("Model invoke" , parent_span , attributes )
329
360
330
361
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
332
363
) -> None :
333
364
"""End a model invocation span with results and metrics.
334
365
@@ -347,9 +378,7 @@ def end_model_invoke_span(
347
378
348
379
self ._end_span (span , attributes , error )
349
380
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 ]:
353
382
"""Start a new span for a tool call.
354
383
355
384
Args:
@@ -374,7 +403,7 @@ def start_tool_call_span(
374
403
return self ._start_span (span_name , parent_span , attributes )
375
404
376
405
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
378
407
) -> None :
379
408
"""End a tool call span with results.
380
409
@@ -402,10 +431,10 @@ def end_tool_call_span(
402
431
def start_event_loop_cycle_span (
403
432
self ,
404
433
event_loop_kwargs : Any ,
405
- parent_span : Optional [trace . Span ] = None ,
434
+ parent_span : Optional [Span ] = None ,
406
435
messages : Optional [Messages ] = None ,
407
436
** kwargs : Any ,
408
- ) -> Optional [trace . Span ]:
437
+ ) -> Optional [Span ]:
409
438
"""Start a new span for an event loop cycle.
410
439
411
440
Args:
@@ -436,7 +465,7 @@ def start_event_loop_cycle_span(
436
465
437
466
def end_event_loop_cycle_span (
438
467
self ,
439
- span : trace . Span ,
468
+ span : Span ,
440
469
message : Message ,
441
470
tool_result_message : Optional [Message ] = None ,
442
471
error : Optional [Exception ] = None ,
@@ -466,7 +495,7 @@ def start_agent_span(
466
495
tools : Optional [list ] = None ,
467
496
custom_trace_attributes : Optional [Mapping [str , AttributeValue ]] = None ,
468
497
** kwargs : Any ,
469
- ) -> Optional [trace . Span ]:
498
+ ) -> Optional [Span ]:
470
499
"""Start a new span for an agent invocation.
471
500
472
501
Args:
@@ -506,7 +535,7 @@ def start_agent_span(
506
535
507
536
def end_agent_span (
508
537
self ,
509
- span : trace . Span ,
538
+ span : Span ,
510
539
response : Optional [AgentResult ] = None ,
511
540
error : Optional [Exception ] = None ,
512
541
) -> None :
@@ -549,6 +578,7 @@ def get_tracer(
549
578
otlp_endpoint : Optional [str ] = None ,
550
579
otlp_headers : Optional [Dict [str , str ]] = None ,
551
580
enable_console_export : Optional [bool ] = None ,
581
+ tracer_provider : Optional [trace_api .TracerProvider ] = None ,
552
582
) -> Tracer :
553
583
"""Get or create the global tracer.
554
584
@@ -557,18 +587,22 @@ def get_tracer(
557
587
otlp_endpoint: OTLP endpoint URL for sending traces.
558
588
otlp_headers: Headers to include with OTLP requests.
559
589
enable_console_export: Whether to also export traces to console.
590
+ tracer_provider: Optional existing TracerProvider to use instead of creating a new one.
560
591
561
592
Returns:
562
593
The global tracer instance.
563
594
"""
564
595
global _tracer_instance
565
596
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
+ ):
567
600
_tracer_instance = Tracer (
568
601
service_name = service_name ,
569
602
otlp_endpoint = otlp_endpoint ,
570
603
otlp_headers = otlp_headers ,
571
604
enable_console_export = enable_console_export ,
605
+ tracer_provider = tracer_provider ,
572
606
)
573
607
574
608
return _tracer_instance
0 commit comments