Repository navigation
[Bug]: Align Execution log correlation and complete Invocation handler MDC #858
Description
Expected Behavior
With the Execution view's built-in log enrichment enabled, durable log records should identify their logical workflow or operation:
| Log location | Expected spanId |
|---|---|
| Top-level handler, including before a durable wait and after resume | Workflow span |
| Step body, including successive retry attempts | Step operation span |
| Child-context body | Child operation span |
| Step body inside a child context | That step's operation span |
For the Invocation view, handler-level logs should carry the current Invocation span ID, while step and child-context logs retain their existing span mapping. In both views, emitted durable logs should include traceId, the view-appropriate spanId, and otelTraceSampled derived from the corresponding span's effective sampling flag. SLF4J MDC represents the flag as the string "true" or "false"; it must not be hard-coded to "true". This is a log-correlation change: retain attempt spans, active tracing scopes, and span parentage. Workflow should continue to be exported once at terminal completion; log correlation can use its stable identity before it is exported.
Actual Behavior
The Execution plugin's invocation-start hook writes only traceId to MDC, leaving top-level spanId and otelTraceSampled unset by the plugin. Step callbacks activate their attempt span and inject its ID into MDC. Child-context bodies already inject their operation span.
onInvocationStart()writes onlytraceId.onUserFunctionStart()uses the child operation context for CONTEXT and the attempt span for STEP.
This report concerns the plugin's MDC enrichment with an MDC-capable SLF4J binding and layout. Additional logging instrumentation can inject fields independently. Having Workflow active in the OTel context does not itself populate its ID in MDC.
The Invocation plugin has the same handler-level field gap: InvocationOtelPlugin.onInvocationStart() also injects only traceId. Step and child callbacks instead use MdcSpanEnricher.inject(), which supplies all three correlation fields.
An active OTel context alone does not populate these MDC keys. The handler scope may preserve a compatible ambient Lambda span, and Execution mode can activate Workflow. Handler enrichment must therefore use the span required by the selected view, rather than blindly copying Span.current() or changing tracing parentage to obtain the intended MDC value.
Local Reproduction Evidence
Two focused local tests exercised the public LocalDurableTestRunner and context.getLogger() path, once with each OTel view, using a sampled extracted context and the existing Logback SLF4J binding to capture MDC before layout. The handler logged before a step, inside the step, inside a child context, and again at the top level before a three-second durable wait.
In both modes:
- The top-level before/between records contained
traceId, but neitherspanIdnorotelTraceSampled, even though the active OTel context was valid and sampled. - Step and child records contained all three fields, with
spanIdmatching their active attempt or child span andotelTraceSampled="true".
Both diagnostic tests passed assertions describing the current gap. They ran against Java SDK source 4b1aa82b0d898fea495cda45760ce3737fbb341e; the implicated enrichment paths are unchanged at the inspected main commit 1a501400d17532a30e4627f06ea516e0bed7b0cd. This is local MDC evidence, not a Log4j2 cloud validation.
The separate defect that can suppress a top-level log immediately after a replayed terminal wait is outside this addition. The correlation requirements below apply to emitted records; resume-path field validation can execute a new durable operation before the top-level log to avoid conflating the two defects.
Steps to Reproduce
The focused field-gap reproduction above has been run locally. The following broader Execution-mapping recipe, including nested steps and retries, remains a validation plan and has not been run in full for this report.
- Enable the Execution plugin and its built-in log enrichment, and capture the logger's correlation fields plus exported spans.
- Emit a durable log at the top level before any operations.
- Run a named step that logs from its body, and a named child context that logs from its body and a nested step.
- Perform a durable wait that suspends the invocation, then emit another top-level log after resume.
- Compare each record's
spanIdwith the corresponding Workflow, Step operation, Child operation, and Attempt spans. - Add a retrying step to check that records from different attempts use the same Step operation
spanId. - Repeat the handler-level field checks with the Invocation plugin: top-level records must use the current Invocation span ID; step and child mappings remain unchanged.
- Repeat with sampled and unsampled trace contexts and with a compatible ambient Lambda span. Check the sampling field against the corresponding span, rather than assuming it is always
"true".
SDK Version
Java otel-plugin, public main inspected at 1a501400d17532a30e4627f06ea516e0bed7b0cd.
Java Version
Java 17 for the two focused local MDC probes. The broader Execution-mapping recipe above remains a source-derived validation plan.
Is this a regression?
Unknown; no last working version has been established.
Acceptance Criteria
-
In the Execution view, emitted top-level logs before suspension and after resume carry the same Workflow
spanId, including when a compatible Lambda span was already active. -
In the Execution view, step-body logs carry the corresponding Step operation
spanIdacross retries; a nested step uses its own operation span. -
Child-body logs retain the Child operation
spanId; leaving a nested scope restores the enclosing logical scope. -
In the Invocation view, emitted handler-level records carry the current Invocation span ID; preserve the existing step-attempt and child-context correlation mapping.
-
In both views, emitted records include
traceId, the view-appropriatespanId, andotelTraceSampledreflecting the corresponding span's effective sampling flag. Cover both sampled and unsampled contexts. -
Replay suppresses duplicate durable logs from completed work.
-
Attempt spans and tracing topology remain intact, and Workflow is exported once at terminal completion.
-
Add regression coverage through the SDK's durable logger and real handler execution path, including both views, compatible ambient Lambda context, concurrent-invocation isolation, enrichment disabled, and restoration of enclosing MDC fields after nested/worker scopes.
-
Update the Execution-view log-correlation documentation and any tests that assert the previous mapping. Update the Invocation documentation to describe complete handler-level MDC fields instead of traceId-only enrichment.
-
MDC cleanup must prevent completed operations' IDs from remaining on subsequent records, and prevent state leaking between invocations.
Related Issues
-
[Bug]: Execution OTel view retains the ended attempt spanId in MDC #815 tracks MDC cleanup immediately after an attempt ends. This issue defines the Execution view's log-to-span mapping and completes handler-level correlation fields in both views; the fixes should be coordinated.
-
Python alignment: [Bug]: Align Execution view log correlation with Workflow and operation spans aws-durable-execution-sdk-python#893
Activity
[-][Bug]: Align Execution view log correlation with Workflow and operation spans[/-][+][Bug]: Align Execution log correlation and complete Invocation handler MDC[/+]