Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions .github/scripts/tests/test_opentelemetry_conformance_workflow.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
from pathlib import Path


WORKFLOW_PATH = (
Path(__file__).parents[2] / "workflows" / "opentelemetry-conformance-tests.yml"
)


def test_opentelemetry_conformance_caller_uses_current_workflow_contract() -> None:
workflow = WORKFLOW_PATH.read_text()

assert "otlp_endpoint:" not in workflow

for secret_name in (
"DATADOG_ACCESS_TOKEN",
"DATADOG_API_KEY",
"DATADOG_APPLICATION_KEY",
):
mapping = f"{secret_name}: ${{{{ secrets.{secret_name} }}}}"
assert mapping in workflow

for obsolete_secret_name in (
"DD_API_KEY",
"DD_APPLICATION_KEY",
"DATADOG_OTLP_HEADERS",
):
assert f"{obsolete_secret_name}:" not in workflow
11 changes: 3 additions & 8 deletions .github/workflows/opentelemetry-conformance-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,6 @@ on:
required: true
default: us-west-2
type: string
otlp_endpoint:
description: OTLP ingest endpoint for community-layer jobs
required: false
type: string
conformance_test_ref:
description: Conformance test commit SHA or branch name
required: true
Expand All @@ -57,14 +53,13 @@ jobs:
phase: ${{ inputs.phase || 'short' }}
delay_seconds: ${{ inputs.delay_seconds || '82800' }}
aws_region: ${{ inputs.aws_region || 'us-west-2' }}
otlp_endpoint: ${{ inputs.otlp_endpoint || '' }}
conformance_test_ref: ${{ inputs.conformance_test_ref || 'main' }}
python_sdk_ref: ${{ github.event.pull_request.head.sha || github.sha }}
secrets:
CONFORMANCE_TEST_ROLE_ARN: ${{ secrets.TEST_ROLE_ARN }}
CONFORMANCE_TEST_ACCOUNT_ID: ${{ secrets.TEST_ACCOUNT_ID }}
CONFORMANCE_TEST_LAMBDA_EXECUTION_ROLE_ARN: ${{ secrets.TEST_LAMBDA_EXECUTION_ROLE_ARN }}
DASH0_AUTH_TOKEN: ${{ secrets.DASH0_AUTH_TOKEN }}
DD_API_KEY: ${{ secrets.DD_API_KEY }}
DD_APPLICATION_KEY: ${{ secrets.DD_APPLICATION_KEY }}
DATADOG_OTLP_HEADERS: ${{ secrets.DATADOG_OTLP_HEADERS }}
DATADOG_ACCESS_TOKEN: ${{ secrets.DATADOG_ACCESS_TOKEN }}
DATADOG_API_KEY: ${{ secrets.DATADOG_API_KEY }}
DATADOG_APPLICATION_KEY: ${{ secrets.DATADOG_APPLICATION_KEY }}
3 changes: 3 additions & 0 deletions .github/workflows/test-parser.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,14 @@ on:
- '.github/scripts/build_lambda_layer.py'
- '.github/scripts/parse_sdk_branch.py'
- '.github/scripts/tests/**'
- '.github/workflows/opentelemetry-conformance-tests.yml'
push:
branches: [ main ]
paths:
- '.github/scripts/build_lambda_layer.py'
- '.github/scripts/parse_sdk_branch.py'
- '.github/scripts/tests/**'
- '.github/workflows/opentelemetry-conformance-tests.yml'

permissions:
contents: read
Expand All @@ -29,4 +31,5 @@ jobs:
run: |
python -m pytest \
.github/scripts/tests/test_build_lambda_layer.py \
.github/scripts/tests/test_opentelemetry_conformance_workflow.py \
.github/scripts/tests/test_parse_sdk_branch.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,7 @@

from typing import Any

from aws_durable_execution_sdk_python_otel import (
InvocationOtelPlugin,
OtelPluginConfig,
ProviderSource,
)
from aws_durable_execution_sdk_python_otel import InvocationOtelPlugin

from aws_durable_execution_sdk_python import StepContext
from aws_durable_execution_sdk_python.context import (
Expand Down Expand Up @@ -48,11 +44,7 @@ def greet_in_child(child_context: DurableContext, name: str) -> str:
return result


@durable_execution(
plugins=[
InvocationOtelPlugin(OtelPluginConfig(provider_source=ProviderSource.GLOBAL))
]
)
@durable_execution(plugins=[InvocationOtelPlugin()])
def handler(_event: Any, context: DurableContext) -> str:
# Logged at the top level: enriched with the invocation span_id.
context.logger.info("Workflow started")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,7 @@

from typing import Any

from aws_durable_execution_sdk_python_otel import (
InvocationOtelPlugin,
OtelPluginConfig,
ProviderSource,
)
from aws_durable_execution_sdk_python_otel import InvocationOtelPlugin

from aws_durable_execution_sdk_python import StepContext
from aws_durable_execution_sdk_python.config import Duration
Expand Down Expand Up @@ -36,11 +32,7 @@ def add_numbers_in_child(child_context: DurableContext, a: int, b: int):
return result


@durable_execution(
plugins=[
InvocationOtelPlugin(OtelPluginConfig(provider_source=ProviderSource.GLOBAL))
]
)
@durable_execution(plugins=[InvocationOtelPlugin()])
def handler(_event: Any, context: DurableContext) -> int:
result = 0
for i in range(3):
Expand Down
24 changes: 11 additions & 13 deletions packages/aws-durable-execution-sdk-python-otel/README.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
# AWS Durable Execution SDK - OpenTelemetry Plugin

OpenTelemetry instrumentation plugin for the [AWS Durable Execution SDK for Python](https://github.com/aws/aws-durable-execution-sdk-python). Emits distributed traces that correlate across multiple Lambda invocations of a single durable execution, producing deterministic span and trace IDs so that spans from different invocations are stitched into a single coherent trace.
OpenTelemetry instrumentation plugin for the [AWS Durable Execution SDK for Python](https://github.com/aws/aws-durable-execution-sdk-python). Emits durable execution spans with deterministic workflow and operation IDs while keeping invocation spans in the ambient Lambda trace.

## Features

- **Deterministic Trace IDs**: All invocations of the same durable execution share a single trace, derived from the X-Ray trace header or execution ARN
- **Deterministic Workflow Traces**: Durable operations use an execution-derived trace that is independent of the ambient Lambda/X-Ray trace
Comment thread
zhongkechen marked this conversation as resolved.
- **Ambient Invocation Traces**: Invocation spans inherit the active Lambda or extracted upstream context
- **Span-per-Operation**: Each durable operation (step, wait, invoke) gets its own span with accurate timing
- **Continuation Spans**: Operations completing in a different invocation are linked back to the original span
- **Continuation Spans**: Operations completing in another invocation produce a new correlated span without fabricating an unobserved prior span context
- **Log Correlation**: Enrich application logs with trace ID and span ID for end-to-end observability
- **Configurable Sampling**: Control trace volume via plugin options
- **Self-Contained Setup**: No manual TracerProvider configuration required
- **Provider Integration**: Use the global ADOT provider or supply an explicit SDK `TracerProvider`
- **Provider-Managed Sampling**: Use standard OpenTelemetry or ADOT sampling configuration

## Installation

Expand Down Expand Up @@ -157,7 +158,8 @@ def handler(event: dict, context: DurableContext) -> dict:
return result
```

That's it. The plugin handles TracerProvider setup, deterministic ID generation, and span lifecycle internally.
The ADOT layer supplies the global `TracerProvider`; the plugin handles
deterministic ID generation and span lifecycle.

### 4. Grant Permissions

Expand Down Expand Up @@ -187,10 +189,6 @@ from aws_durable_execution_sdk_python_otel import (

plugin = InvocationOtelPlugin(
OtelPluginConfig(
# Provide your own TracerProvider if you already have one configured.
# When omitted, an OTLP provider is auto-configured (like ExecutionOtelPlugin);
# set use_default_tracer_provider=True to use the global (e.g. ADOT) provider.
tracer_provider=None,
# Use a custom context extractor (default: xray_context_extractor).
context_extractor=xray_context_extractor,
# Custom instrumentation scope name
Expand Down Expand Up @@ -275,12 +273,13 @@ InvocationOtelPlugin(
instrument_name="aws-durable-execution-sdk-python",
enrich_logger=True,
workflow_span_name="Workflow",
# ...and the rest of OtelPluginConfig (use_default_tracer_provider,
# enable_http_instrumentation, exporter_config, propagators).
)
)
```

Pass `tracer_provider=...` when the application owns the OpenTelemetry SDK
provider. When omitted, the globally configured provider is used.

### `DeterministicIdGenerator`

A custom OpenTelemetry `IdGenerator` that produces reproducible trace and span IDs from execution metadata. Exported for advanced use cases.
Expand Down Expand Up @@ -309,7 +308,6 @@ setups.
- `aws-durable-execution-sdk-python` >= 1.8.0
- `opentelemetry-api` >= 1.20.0
- `opentelemetry-sdk` >= 1.20.0
- `opentelemetry-exporter-otlp`

## License

Expand Down
10 changes: 0 additions & 10 deletions packages/aws-durable-execution-sdk-python-otel/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,23 +25,13 @@ dependencies = [
"aws-durable-execution-sdk-python>=1.8.0",
"opentelemetry-api>=1.20.0",
"opentelemetry-sdk>=1.20.0",
"opentelemetry-exporter-otlp",
"opentelemetry-propagator-aws-xray",
]

[project.entry-points."aws_durable_execution.plugins"]
otel-invocation = "aws_durable_execution_sdk_python_otel.plugin_provider:INVOCATION_OTEL_PLUGIN_PROVIDER"
otel-execution = "aws_durable_execution_sdk_python_otel.plugin_provider:EXECUTION_OTEL_PLUGIN_PROVIDER"

[project.optional-dependencies]
# Instrumentation used by ExecutionOtelPlugin's auto-configured provider path.
# Kept optional so the InvocationOtelPlugin (ADOT / global provider) install
# stays lean; the instrumentations module degrades gracefully when absent.
instrumentation = [
"opentelemetry-instrumentation-botocore",
"opentelemetry-instrumentation-urllib3",
]

[project.urls]
Documentation = "https://github.com/aws/aws-durable-execution-sdk-python#readme"
Issues = "https://github.com/aws/aws-durable-execution-sdk-python/issues"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,6 @@
)
from aws_durable_execution_sdk_python_otel.otel_plugin_config import (
OtelPluginConfig,
ExporterConfig,
ProviderSource,
)
from aws_durable_execution_sdk_python_otel.instrumentations import (
register_standalone_instrumentations,
)
from aws_durable_execution_sdk_python_otel.log_filter import (
OtelContextLogFilter,
Expand All @@ -41,16 +36,13 @@
"DeterministicIdGenerator",
"ExecutionOtelPlugin",
"OtelPluginConfig",
"ExporterConfig",
"InvocationOtelPlugin",
"OtelContextLogFilter",
"ProviderResult",
"ProviderSource",
"create_tracer_provider",
"derive_workflow_span_id",
"install_log_filter",
"operation_id_to_span_id",
"register_standalone_instrumentations",
"w3c_client_context_extractor",
"xray_context_extractor",
]
Loading
Loading