Add workflow-rooted ExecutionOtelPlugin and fix OtelPlugin status parity - #570
Draft
SilanHe wants to merge 6 commits into
Draft
Add workflow-rooted ExecutionOtelPlugin and fix OtelPlugin status parity#570SilanHe wants to merge 6 commits into
SilanHe wants to merge 6 commits into
Conversation
Port the Workflow-rooted ExecutionOtelPlugin from the Python/JS SDKs into the otel-plugin module, alongside the existing invocation-rooted OtelPlugin: - ExecutionOtelPlugin: deterministic Workflow root span (exported once per execution, terminal-only), invocation span as its child, operations parented to the Workflow span and linked to the invocation span; deterministic span IDs stitch suspended/resumed operations into a single logical span. - OtelPlugin parity fix: invocation-span status now maps SUCCEEDED/PENDING -> OK and RETRYING/FAILED -> ERROR (was FAILED-only). - DeterministicIdGenerator: add setNextSpanId(raw) and generateWorkflowSpanId(). - SpanAttributes: add durable.execution.status for the Workflow span. - Tests: new 16-test ExecutionOtelPluginTest; update OtelPluginTest SUCCEEDED assertion to OK. otel-plugin 105/105, core SDK 1098/1098.
Rename the invocation-rooted plugin to InvocationOtelPlugin to match the
Python/JS SDK naming, now that ExecutionOtelPlugin (workflow-rooted) exists
alongside it. Prerelease, so no backwards-compat alias is kept.
- git mv OtelPlugin{,Test,IntegrationTest}.java -> InvocationOtelPlugin*.java
- Update all references: MdcSpanEnricher(+Test), ExecutionOtelPlugin javadoc,
4 example handlers, otel-plugin/README.md, AGENTS.md.
- ExecutionOtelPlugin and all other classes unchanged.
Build: otel-plugin + examples green.
SilanHe
commented
Jul 27, 2026
| 3. Register `InvocationOtelPlugin` in your handler's `DurableConfig` | ||
| 4. Grant X-Ray write permissions | ||
|
|
||
| ### 1. ADOT Lambda Layer |
Contributor
Author
There was a problem hiding this comment.
I'll update the README in a separate PR
The workflow root span previously relied on OTel's implicit INTERNAL default; set it explicitly via setSpanKind(SpanKind.INTERNAL) and add a test asserting it. Workflow and invocation span status mappings are unchanged (already: Workflow SUCCEEDED->OK/FAILED->ERROR, non-terminal->UNSET/not-exported; Invocation SUCCEEDED+PENDING->OK, RETRYING+FAILED->ERROR).
Match the JS/Python ExecutionOtelPlugin export-once semantics: onInvocationEnd no longer force-ends still-open operation spans as PENDING. An operation left open when the invocation suspends is materialized exactly once (deterministic span ID + link to the completing invocation) when onOperationEnd fires later. Per-invocation state is still reset and lingering attempt scopes are closed defensively, but their spans are no longer force-ended. InvocationOtelPlugin (legacy, invocation-rooted) keeps its PENDING-at-end behavior unchanged. Tests: replace operationNotCompleted_endedPendingAtInvocationEnd with operationNotCompleted_notEndedAtInvocationEnd and add operationOpenedThenCompletedNextInvocation_exportedOnceOnOperationEnd.
Invocation span status now maps SUCCEEDED/PENDING -> OK, FAILED -> ERROR, and RETRYING -> UNSET (previously ERROR), in both ExecutionOtelPlugin and InvocationOtelPlugin. RETRYING is left UNSET because the plugin interface does not expose whether the invocation/workflow was STOPPED or TIMED_OUT versus retried for a transient error, so ERROR cannot be asserted reliably. Workflow/execution span mapping is unchanged (FAILED->ERROR, SUCCEEDED->OK, RETRY/PENDING->UNSET via terminal-only export) and the workflow span kind remains explicitly INTERNAL. Class javadoc updated; tests updated/added for the RETRYING->UNSET behavior in both plugins.
The prior comment said the continuation span 'stitches to the original' with the same deterministic ID. That is misleading: OTel spans cannot be edited after export, and the prior invocation drops its in-memory span un-exported. This branch is simply the single export of the operation's span, using its deterministic ID. Comment-only change.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.
Issue Link, if available
Description
Port the Workflow-rooted ExecutionOtelPlugin from the Python/JS SDKs into the otel-plugin module, alongside the existing invocation-rooted OtelPlugin:
Demo/Screenshots
Checklist
Testing
Unit Tests
Have unit tests been written for these changes?
Integration Tests
Have integration tests been written for these changes?
Examples
Has a new example been added for the change? (if applicable)