1111/**
1212 * Generates deterministic trace and span IDs for durable execution observability.
1313 *
14- * <p>All invocations of the same execution share a single trace ID (derived from the execution ARN). Operations get
15- * stable span IDs derived from the execution ARN + operation ID, ensuring the same operation produces the same span
16- * across invocations.
14+ * <p>Trace ID resolution order:
1715 *
18- * <p>When no pending operation ID is set, falls back to random generation (standard OTel behavior).
16+ * <ol>
17+ * <li>If an extracted trace ID is set (from {@code _X_AMZN_TRACE_ID}), use it. The durable execution backend
18+ * propagates the same Root to all invocations, so this naturally unifies the trace.
19+ * <li>If no extracted trace ID is available (local tests, non-Lambda environments), derive a deterministic trace ID
20+ * from the execution ARN using SHA-256.
21+ * <li>If neither is set, fall back to random generation.
22+ * </ol>
23+ *
24+ * <p>Span IDs for operations are deterministic (derived from execution ARN + operation ID), ensuring the same operation
25+ * produces the same span across invocations. When no pending operation ID is set, falls back to random generation.
1926 *
2027 * @deprecated This is a preview API that is experimental and may be changed or removed in future releases.
2128 */
@@ -24,18 +31,30 @@ public class DeterministicIdGenerator implements IdGenerator {
2431
2532 private static final IdGenerator RANDOM = IdGenerator .random ();
2633
27- private final AtomicReference <String > executionTraceId = new AtomicReference <>(null );
34+ private final AtomicReference <String > extractedTraceId = new AtomicReference <>(null );
35+ private final AtomicReference <String > arnDerivedTraceId = new AtomicReference <>(null );
2836 private final ThreadLocal <String > pendingSpanOperationId = new ThreadLocal <>();
2937 private final AtomicReference <String > durableExecutionArn = new AtomicReference <>(null );
3038
3139 /**
32- * Sets the execution ARN used for generating deterministic IDs.
40+ * Sets an externally extracted trace ID (e.g., from the X-Ray trace header). This takes highest priority for trace
41+ * ID generation.
42+ *
43+ * @param traceId 32-char lowercase hex trace ID
44+ */
45+ public void setExtractedTraceId (String traceId ) {
46+ this .extractedTraceId .set (traceId );
47+ }
48+
49+ /**
50+ * Sets the execution ARN used for generating deterministic IDs. Computes and caches an ARN-derived trace ID as
51+ * fallback when no extracted trace ID is available.
3352 *
3453 * @param arn the durable execution ARN
3554 */
3655 public void setDurableExecutionArn (String arn ) {
3756 this .durableExecutionArn .set (arn );
38- this .executionTraceId .set (generateTraceIdFromArn (arn ));
57+ this .arnDerivedTraceId .set (generateTraceIdFromArn (arn ));
3958 }
4059
4160 /**
@@ -50,9 +69,6 @@ public void setNextSpanOperationId(String operationId) {
5069 /**
5170 * Generates a deterministic span ID for a given operation ID without consuming the ThreadLocal state.
5271 *
53- * <p>Used for creating non-recording placeholder spans when a parent operation's span context is needed but hasn't
54- * been exported yet.
55- *
5672 * @param operationId the operation ID to derive the span ID from
5773 * @return a deterministic 16-char hex span ID
5874 */
@@ -62,10 +78,17 @@ public String generateSpanIdForOperation(String operationId) {
6278
6379 @ Override
6480 public String generateTraceId () {
65- var cached = executionTraceId .get ();
66- if (cached != null ) {
67- return cached ;
81+ // Priority 1: extracted from X-Ray header (backend propagates same Root across invocations)
82+ var extracted = extractedTraceId .get ();
83+ if (extracted != null ) {
84+ return extracted ;
85+ }
86+ // Priority 2: deterministic from execution ARN (local tests, non-Lambda)
87+ var arnDerived = arnDerivedTraceId .get ();
88+ if (arnDerived != null ) {
89+ return arnDerived ;
6890 }
91+ // Priority 3: random fallback
6992 return RANDOM .generateTraceId ();
7093 }
7194
@@ -79,27 +102,19 @@ public String generateSpanId() {
79102 return RANDOM .generateSpanId ();
80103 }
81104
82- /**
83- * Generates a deterministic trace ID from an execution ARN.
84- *
85- * <p>Uses SHA-256 hash truncated to 32 hex chars (128 bits) for the trace ID.
86- */
105+ /** Generates a deterministic trace ID from an execution ARN using SHA-256 truncated to 32 hex chars. */
87106 private String generateTraceIdFromArn (String arn ) {
88107 var hash = sha256 (arn );
89- // Trace ID is 32 hex chars (16 bytes)
90108 return hash .substring (0 , 32 );
91109 }
92110
93111 /**
94- * Generates a deterministic span ID from the execution ARN + operation ID.
95- *
96- * <p>Uses SHA-256 hash truncated to 16 hex chars (64 bits) for the span ID.
112+ * Generates a deterministic span ID from the execution ARN + operation ID using SHA-256 truncated to 16 hex chars.
97113 */
98114 private String generateSpanIdFromOperation (String operationId ) {
99115 var arn = durableExecutionArn .get ();
100116 var input = arn != null ? arn + ":" + operationId : operationId ;
101117 var hash = sha256 (input );
102- // Span ID is 16 hex chars (8 bytes)
103118 return hash .substring (0 , 16 );
104119 }
105120
0 commit comments