Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 6076a17

Browse files
committedJun 12, 2025·
chore: allow custom tracer provider to Agent
1 parent 264f511 commit 6076a17

File tree

4 files changed

+123
-44
lines changed

4 files changed

+123
-44
lines changed
 

‎src/strands/agent/agent.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
from uuid import uuid4
2121

2222
from opentelemetry import trace
23+
from opentelemetry.sdk.trace import TracerProvider
2324

2425
from ..event_loop.event_loop import event_loop_cycle
2526
from ..handlers.callback_handler import CompositeCallbackHandler, PrintingCallbackHandler, null_callback_handler
@@ -216,6 +217,7 @@ def __init__(
216217
record_direct_tool_call: bool = True,
217218
load_tools_from_directory: bool = True,
218219
trace_attributes: Optional[Mapping[str, AttributeValue]] = None,
220+
tracer_provider: Optional[TracerProvider] = None,
219221
):
220222
"""Initialize the Agent with the specified configuration.
221223
@@ -248,6 +250,7 @@ def __init__(
248250
load_tools_from_directory: Whether to load and automatically reload tools in the `./tools/` directory.
249251
Defaults to True.
250252
trace_attributes: Custom trace attributes to apply to the agent's trace span.
253+
tracer_provider: Custom trace provider to apply to the agents' tracer.
251254
252255
Raises:
253256
ValueError: If max_parallel_tools is less than 1.
@@ -306,7 +309,7 @@ def __init__(
306309
self.event_loop_metrics = EventLoopMetrics()
307310

308311
# Initialize tracer instance (no-op if not configured)
309-
self.tracer = get_tracer()
312+
self.tracer = get_tracer(tracer_provider=tracer_provider)
310313
self.trace_span: Optional[trace.Span] = None
311314

312315
self.tool_caller = Agent.ToolCaller(self)

‎src/strands/agent/conversation_manager/sliding_window_conversation_manager.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ def reduce_context(self, agent: "Agent", e: Optional[Exception] = None) -> None:
140140
if results_truncated:
141141
logger.debug("message_index=<%s> | tool results truncated", last_message_idx_with_tool_results)
142142
return
143-
143+
144144
# Try to trim index id when tool result cannot be truncated anymore
145145
# If the number of messages is less than the window_size, then we default to 2, otherwise, trim to window size
146146
trim_index = 2 if len(messages) <= self.window_size else len(messages) - self.window_size

‎src/strands/telemetry/tracer.py

Lines changed: 63 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,16 @@
1111
from importlib.metadata import version
1212
from 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
1517
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
18+
from opentelemetry.propagators.composite import CompositePropagator
1619
from opentelemetry.sdk.resources import Resource
17-
from opentelemetry.sdk.trace import TracerProvider
20+
from opentelemetry.sdk.trace import TracerProvider as SDKTracerProvider
1821
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
2024

2125
from ..agent.agent_result import AgentResult
2226
from ..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

Comments
 (0)
Please sign in to comment.