Skip to content

Commit 4e5fe40

Browse files
committed
feat(otel): InvocationOtelPlugin uses config
Replace InvocationOtelPlugin's legacy positional kwargs with a single OtelPluginConfig, mirroring ExecutionOtelPlugin and the JS SDK plugins. This adds use_default_tracer_provider, enable_http_instrumentation, exporter_config, and propagators to the Invocation plugin. Also align the default provider with JS: when no provider is supplied the plugin auto-configures an OTLP provider (default_use_global=False) instead of the global/ADOT provider. Pass use_default_tracer_provider =True for the global (e.g. ADOT) provider. Migrated test call sites and README snippets to OtelPluginConfig. 105/105 otel tests pass; ruff and mypy clean. BREAKING CHANGE: InvocationOtelPlugin no longer accepts positional or keyword arguments; pass an OtelPluginConfig instead, e.g. InvocationOtelPlugin(OtelPluginConfig(tracer_provider=...)).
1 parent 2230b63 commit 4e5fe40

5 files changed

Lines changed: 123 additions & 64 deletions

File tree

‎packages/aws-durable-execution-sdk-python-otel/README.md‎

Lines changed: 27 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -168,21 +168,25 @@ See the [ADOT sampling configuration](https://aws-otel.github.io/docs/getting-st
168168
```python
169169
from aws_durable_execution_sdk_python_otel import (
170170
InvocationOtelPlugin,
171+
OtelPluginConfig,
171172
xray_context_extractor,
172173
)
173174

174175
plugin = InvocationOtelPlugin(
175-
# Provide your own TracerProvider if you already have one configured.
176-
# Defaults to the globally configured tracer provider.
177-
trace_provider=None,
178-
# Use a custom context extractor (default: xray_context_extractor).
179-
context_extractor=xray_context_extractor,
180-
# Custom instrumentation scope name
181-
# (default: "aws-durable-execution-sdk-python").
182-
instrument_name="my-service",
183-
# Install a root-logger filter that stamps trace context onto every
184-
# log record (default: True).
185-
enrich_logger=True,
176+
OtelPluginConfig(
177+
# Provide your own TracerProvider if you already have one configured.
178+
# When omitted, an OTLP provider is auto-configured (like ExecutionOtelPlugin);
179+
# set use_default_tracer_provider=True to use the global (e.g. ADOT) provider.
180+
tracer_provider=None,
181+
# Use a custom context extractor (default: xray_context_extractor).
182+
context_extractor=xray_context_extractor,
183+
# Custom instrumentation scope name
184+
# (default: "aws-durable-execution-sdk-python").
185+
instrument_name="my-service",
186+
# Install a root-logger filter that stamps trace context onto every
187+
# log record (default: True).
188+
enrich_logger=True,
189+
)
186190
)
187191
```
188192

@@ -193,15 +197,16 @@ The plugin supports multiple strategies for extracting upstream trace context:
193197
```python
194198
from aws_durable_execution_sdk_python_otel import (
195199
InvocationOtelPlugin,
200+
OtelPluginConfig,
196201
w3c_client_context_extractor,
197202
xray_context_extractor,
198203
)
199204

200205
# Default: X-Ray trace header (recommended for most Lambda deployments)
201-
InvocationOtelPlugin(context_extractor=xray_context_extractor)
206+
InvocationOtelPlugin(OtelPluginConfig(context_extractor=xray_context_extractor))
202207

203208
# W3C Trace Context via clientContext (requires backend propagation support)
204-
InvocationOtelPlugin(context_extractor=w3c_client_context_extractor)
209+
InvocationOtelPlugin(OtelPluginConfig(context_extractor=w3c_client_context_extractor))
205210
```
206211

207212
### Log Correlation
@@ -251,10 +256,15 @@ The main plugin class. Implements `DurableInstrumentationPlugin` from `aws_durab
251256

252257
```python
253258
InvocationOtelPlugin(
254-
trace_provider=None,
255-
context_extractor=None,
256-
instrument_name="aws-durable-execution-sdk-python",
257-
enrich_logger=True,
259+
OtelPluginConfig(
260+
tracer_provider=None,
261+
context_extractor=None,
262+
instrument_name="aws-durable-execution-sdk-python",
263+
enrich_logger=True,
264+
workflow_span_name="Workflow",
265+
# ...and the rest of OtelPluginConfig (use_default_tracer_provider,
266+
# enable_http_instrumentation, exporter_config, propagators).
267+
)
258268
)
259269
```
260270

‎packages/aws-durable-execution-sdk-python-otel/src/aws_durable_execution_sdk_python_otel/invocation_plugin.py‎

Lines changed: 59 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,11 @@
4545
)
4646
from aws_durable_execution_sdk_python_otel.log_filter import install_log_filter
4747
from aws_durable_execution_sdk_python_otel.otel_plugin_config import (
48-
DEFAULT_WORKFLOW_SPAN_NAME,
48+
OtelPluginConfig,
49+
)
50+
from aws_durable_execution_sdk_python_otel.provider import create_tracer_provider
51+
from aws_durable_execution_sdk_python_otel.instrumentations import (
52+
register_standalone_instrumentations,
4953
)
5054

5155

@@ -80,42 +84,60 @@ class InvocationOtelPlugin(DurableInstrumentationPlugin):
8084
original logical operation.
8185
8286
Args:
83-
trace_provider: OpenTelemetry tracer provider used to create spans.
84-
Optional; when omitted, the globally configured tracer provider
85-
(``opentelemetry.trace.get_tracer_provider()``) is used.
86-
context_extractor: Optional extractor for upstream context. Defaults to
87-
AWS X-Ray header extraction.
88-
instrument_name: Instrumentation scope name registered with the tracer.
87+
config: Shared plugin configuration (the same OtelPluginConfig accepted
88+
by ExecutionOtelPlugin). When omitted, defaults are used (X-Ray
89+
extractor, "Workflow" span name, log enrichment on). When no provider
90+
is configured, an OTLP provider is auto-configured (matching
91+
ExecutionOtelPlugin and the JS SDK plugins); set
92+
``use_default_tracer_provider=True`` on the config to use the globally
93+
configured tracer provider instead (e.g. the ADOT Lambda layer).
8994
"""
9095

9196
DEFAULT_INSTRUMENT_NAME = "aws-durable-execution-sdk-python"
9297

93-
def __init__(
94-
self,
95-
trace_provider: SdkTracerProvider | None = None,
96-
context_extractor: ContextExtractor | None = None,
97-
instrument_name: str = DEFAULT_INSTRUMENT_NAME,
98-
enrich_logger: bool = True,
99-
workflow_span_name: str = DEFAULT_WORKFLOW_SPAN_NAME,
100-
) -> None:
101-
"""Initialize the plugin with an OpenTelemetry tracer provider.
98+
def __init__(self, config: OtelPluginConfig | None = None) -> None:
99+
"""Initialize the plugin from a shared OtelPluginConfig.
100+
101+
Accepts the same OtelPluginConfig as ExecutionOtelPlugin so both plugins
102+
share one configuration surface (context extractor, instrumentation
103+
name, provider selection, exporter/propagator settings, log
104+
enrichment). Like ExecutionOtelPlugin and the JS SDK plugins, it
105+
auto-configures an OTLP provider when nothing is supplied; pass
106+
``use_default_tracer_provider=True`` to use the globally configured
107+
(e.g. ADOT) provider instead.
102108
103109
The tracer provider is configured with this plugin's deterministic ID
104110
generator so spans for a durable execution share stable trace and
105-
logical operation identifiers. When no provider is supplied, the
106-
globally configured tracer provider is used.
111+
logical operation identifiers.
107112
108-
When enrich_logger is enabled (default), the plugin installs a logging
109-
filter on the root logger at invocation start that stamps the active
110-
OTel trace context onto every emitted log record.
113+
When ``enrich_logger`` is enabled (default), the plugin installs a
114+
logging filter that stamps the active OTel trace context onto every
115+
emitted log record.
111116
"""
112-
self._enrich_logger = enrich_logger
113-
self._workflow_span_name = workflow_span_name
117+
self._config = config or OtelPluginConfig()
114118
self._context_extractor: ContextExtractor = (
115-
context_extractor or xray_context_extractor
119+
self._config.context_extractor or xray_context_extractor
116120
)
121+
self._workflow_span_name = self._config.workflow_span_name
122+
self._enrich_logger = self._config.enrich_logger
123+
124+
# Like ExecutionOtelPlugin (and the JS SDK plugins), InvocationOtelPlugin
125+
# auto-configures an OTLP provider when nothing is supplied. Resolve the
126+
# effective "use the global provider" decision up front so that
127+
# instrumentation registration matches what create_tracer_provider does.
128+
self._use_default = self._config.use_default_tracer_provider
129+
if self._use_default is None:
130+
self._use_default = False
131+
132+
self._id_generator = DeterministicIdGenerator()
133+
result = create_tracer_provider(
134+
self._config,
135+
id_generator=self._id_generator,
136+
default_use_global=False,
137+
)
138+
self._provider = result.tracer_provider
139+
self._owns_provider = result.owns_provider
117140

118-
self._provider = trace_provider or trace.get_tracer_provider()
119141
# Deterministic trace stitching requires the SDK TracerProvider, which
120142
# exposes id_generator/sampler. The API's default ProxyTracerProvider
121143
# (returned before an SDK provider is configured) does not. Rather than
@@ -128,16 +150,25 @@ def __init__(
128150
self._provider
129151
)
130152
else:
131-
self._id_generator = DeterministicIdGenerator()
132153
logger.warning(
133154
"InvocationOtelPlugin expected an SDK TracerProvider "
134155
"(opentelemetry.sdk.trace.TracerProvider) but got %s. Spans will "
135156
"not use deterministic IDs. "
136157
"Ensure the OpenTelemetry SDK is configured (e.g. via the ADOT "
137-
"Lambda layer) or pass an explicit trace_provider.",
158+
"Lambda layer) or pass an explicit tracer_provider.",
138159
type(self._provider).__name__,
139160
)
140-
self._tracer: Tracer = self._provider.get_tracer(instrument_name)
161+
self._tracer: Tracer = self._provider.get_tracer(self._config.instrument_name)
162+
163+
try:
164+
register_standalone_instrumentations(
165+
self._config,
166+
self._provider if self._owns_provider else None,
167+
owns_provider=self._owns_provider,
168+
use_default_tracer_provider=self._use_default,
169+
)
170+
except Exception:
171+
logger.exception("Failed to register standalone instrumentations")
141172

142173
# per invocation status:
143174
self._execution_arn = ""

‎packages/aws-durable-execution-sdk-python-otel/tests/test_invocation_plugin.py‎

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
operation_id_to_span_id,
3737
)
3838
from aws_durable_execution_sdk_python_otel.invocation_plugin import InvocationOtelPlugin
39+
from aws_durable_execution_sdk_python_otel.otel_plugin_config import OtelPluginConfig
3940

4041

4142
START_TIME = datetime(2024, 1, 2, 3, 4, 5, tzinfo=UTC)
@@ -63,8 +64,10 @@ def _create_plugin() -> tuple[InvocationOtelPlugin, InMemorySpanExporter]:
6364
trace_provider = TracerProvider()
6465
trace_provider.add_span_processor(SimpleSpanProcessor(exporter))
6566
plugin = InvocationOtelPlugin(
66-
trace_provider=trace_provider,
67-
context_extractor=lambda _: Context(),
67+
OtelPluginConfig(
68+
tracer_provider=trace_provider,
69+
context_extractor=lambda _: Context(),
70+
)
6871
)
6972
return plugin, exporter
7073

@@ -1083,9 +1086,11 @@ def test_workflow_span_name_is_configurable():
10831086
trace_provider = TracerProvider()
10841087
trace_provider.add_span_processor(SimpleSpanProcessor(exporter))
10851088
plugin = InvocationOtelPlugin(
1086-
trace_provider=trace_provider,
1087-
context_extractor=lambda _: Context(),
1088-
workflow_span_name="MyExecution",
1089+
OtelPluginConfig(
1090+
tracer_provider=trace_provider,
1091+
context_extractor=lambda _: Context(),
1092+
workflow_span_name="MyExecution",
1093+
)
10891094
)
10901095
plugin.on_invocation_start(_invocation_start_info())
10911096
plugin.on_invocation_end(_invocation_end_info())

‎packages/aws-durable-execution-sdk-python-otel/tests/test_invocation_plugin_integration.py‎

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
operation_id_to_span_id,
4646
)
4747
from aws_durable_execution_sdk_python_otel.invocation_plugin import InvocationOtelPlugin
48+
from aws_durable_execution_sdk_python_otel.otel_plugin_config import OtelPluginConfig
4849

4950

5051
START_TIME = datetime(2024, 1, 2, 3, 4, 5, tzinfo=UTC)
@@ -188,9 +189,11 @@ def _assert_hierarchy(exporter: InMemorySpanExporter) -> None:
188189
def test_community_layer_full_lifecycle_uses_supplied_provider():
189190
provider, exporter = _provider()
190191
plugin = InvocationOtelPlugin(
191-
trace_provider=provider,
192-
context_extractor=lambda _: Context(),
193-
enrich_logger=False,
192+
OtelPluginConfig(
193+
tracer_provider=provider,
194+
context_extractor=lambda _: Context(),
195+
enrich_logger=False,
196+
)
194197
)
195198

196199
plugin.on_invocation_start(_invocation_start())
@@ -209,8 +212,11 @@ def test_adot_layer_full_lifecycle_uses_global_provider(monkeypatch):
209212
monkeypatch.setattr(trace, "get_tracer_provider", lambda: provider)
210213

211214
plugin = InvocationOtelPlugin(
212-
context_extractor=lambda _: Context(),
213-
enrich_logger=False,
215+
OtelPluginConfig(
216+
use_default_tracer_provider=True,
217+
context_extractor=lambda _: Context(),
218+
enrich_logger=False,
219+
)
214220
)
215221

216222
plugin.on_invocation_start(_invocation_start())
@@ -225,14 +231,18 @@ def test_second_plugin_configures_cached_tracer_generator(monkeypatch):
225231
"""A second handler's Workflow span uses its deterministic trace ID."""
226232
provider, exporter = _provider()
227233
first_plugin = InvocationOtelPlugin(
228-
trace_provider=provider,
229-
context_extractor=lambda _: Context(),
230-
enrich_logger=False,
234+
OtelPluginConfig(
235+
tracer_provider=provider,
236+
context_extractor=lambda _: Context(),
237+
enrich_logger=False,
238+
)
231239
)
232240
target_plugin = InvocationOtelPlugin(
233-
trace_provider=provider,
234-
context_extractor=lambda _: Context(),
235-
enrich_logger=False,
241+
OtelPluginConfig(
242+
tracer_provider=provider,
243+
context_extractor=lambda _: Context(),
244+
enrich_logger=False,
245+
)
236246
)
237247
monkeypatch.setenv("_X_AMZN_TRACE_ID", XRAY_TRACE_HEADER)
238248

‎packages/aws-durable-execution-sdk-python-otel/tests/test_log_filter.py‎

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
install_log_filter,
2424
)
2525
from aws_durable_execution_sdk_python_otel.invocation_plugin import InvocationOtelPlugin
26+
from aws_durable_execution_sdk_python_otel.otel_plugin_config import OtelPluginConfig
2627

2728

2829
START_TIME = datetime(2024, 1, 2, 3, 4, 5, tzinfo=UTC)
@@ -37,9 +38,11 @@ def _create_plugin(
3738
trace_provider = TracerProvider()
3839
trace_provider.add_span_processor(SimpleSpanProcessor(exporter))
3940
plugin = InvocationOtelPlugin(
40-
trace_provider=trace_provider,
41-
context_extractor=lambda _: Context(),
42-
enrich_logger=enrich_logger,
41+
OtelPluginConfig(
42+
tracer_provider=trace_provider,
43+
context_extractor=lambda _: Context(),
44+
enrich_logger=enrich_logger,
45+
)
4346
)
4447
return plugin, exporter
4548

0 commit comments

Comments
 (0)