Skip to content

Commit ab9943c

Browse files
Use W3C Trace Context trace ID as parent ID regardless of propagation style order (#7355)
* Update HttpCodec.java * feat(core): Simplify W3C phase 3 implementation * Added unit tests * feat(context): Improve W3C support Store Datadog last parent ID Allow tags update from TagContext Add Datadog parent header validation * feat(context): Add W3C header override test * fix lastParentId assignment for case when no p tag available * feat(context): Make sure last parent id can't be invalid * Changed comment to be clearer * added more unit test cases for better codecov * Clarified wording on applyTraceContextToFirstContext function Co-authored-by: Bruce Bujon <[email protected]> Co-authored-by: Bruce Bujon <[email protected]>
1 parent 6326167 commit ab9943c

File tree

6 files changed

+164
-27
lines changed

6 files changed

+164
-27
lines changed

dd-trace-core/src/main/java/datadog/trace/core/propagation/ExtractedContext.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,9 @@
1212
*/
1313
public class ExtractedContext extends TagContext {
1414
private final DDTraceId traceId;
15-
private final long spanId;
1615
private final long endToEndStartTime;
1716
private final PropagationTags propagationTags;
17+
private long spanId;
1818

1919
public ExtractedContext(
2020
final DDTraceId traceId,
@@ -66,6 +66,10 @@ public final long getSpanId() {
6666
return spanId;
6767
}
6868

69+
public final void overrideSpanId(final long spanId) {
70+
this.spanId = spanId;
71+
}
72+
6973
public final long getEndToEndStartTime() {
7074
return endToEndStartTime;
7175
}

dd-trace-core/src/main/java/datadog/trace/core/propagation/HttpCodec.java

Lines changed: 54 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
package datadog.trace.core.propagation;
22

3+
import static datadog.trace.api.DDTags.PARENT_ID;
34
import static datadog.trace.api.TracePropagationStyle.TRACECONTEXT;
5+
import static datadog.trace.core.propagation.DatadogHttpCodec.SPAN_ID_KEY;
46

57
import datadog.trace.api.Config;
68
import datadog.trace.api.DD128bTraceId;
79
import datadog.trace.api.DD64bTraceId;
10+
import datadog.trace.api.DDSpanId;
811
import datadog.trace.api.DDTraceId;
912
import datadog.trace.api.TraceConfig;
1013
import datadog.trace.api.TracePropagationStyle;
@@ -233,10 +236,7 @@ public <C> TagContext extract(
233236
if (traceIdMatch(context.getTraceId(), extractedContext.getTraceId())) {
234237
boolean comingFromTraceContext = extracted.getPropagationStyle() == TRACECONTEXT;
235238
if (comingFromTraceContext) {
236-
// Propagate newly extracted W3C tracestate to first valid context
237-
String extractedTracestate =
238-
extractedContext.getPropagationTags().getW3CTracestate();
239-
context.getPropagationTags().updateW3CTracestate(extractedTracestate);
239+
applyTraceContextToFirstContext(context, extractedContext, extractionCache);
240240
}
241241
} else {
242242
// Terminate extracted context and add it as span link
@@ -262,13 +262,48 @@ else if (extracted != null && partialContext == null) {
262262
return null;
263263
}
264264
}
265+
266+
/**
267+
* Applies span ID from W3C trace context over any other valid context previously found.
268+
*
269+
* @param firstContext The first valid context found.
270+
* @param traceContext The trace context to apply.
271+
* @param extractionCache The extraction cache to get quick access to any extra information.
272+
* @param <C> The carrier type.
273+
*/
274+
private <C> void applyTraceContextToFirstContext(
275+
ExtractedContext firstContext,
276+
ExtractedContext traceContext,
277+
ExtractionCache<C> extractionCache) {
278+
// Propagate newly extracted W3C tracestate to first valid context
279+
String extractedTracestate = traceContext.getPropagationTags().getW3CTracestate();
280+
firstContext.getPropagationTags().updateW3CTracestate(extractedTracestate);
281+
// Check if parent spans differ to reconcile them
282+
if (firstContext.getSpanId() != traceContext.getSpanId()) {
283+
// Override parent span id with W3C one
284+
firstContext.overrideSpanId(traceContext.getSpanId());
285+
// Add last parent ID as a span tag (check W3C first, else Datadog)
286+
CharSequence lastParentId = traceContext.getPropagationTags().getLastParentId();
287+
if (lastParentId == null) {
288+
lastParentId = extractionCache.getDatadogSpanIdHex();
289+
}
290+
if (lastParentId != null) {
291+
firstContext.putTag(PARENT_ID, lastParentId.toString());
292+
}
293+
}
294+
}
265295
}
266296

267297
private static class ExtractionCache<C>
268298
implements AgentPropagation.KeyClassifier,
269299
AgentPropagation.ContextVisitor<ExtractionCache<?>> {
270300
/** Cached context key-values (even indexes are header names, odd indexes are header values). */
271301
private final List<String> keysAndValues;
302+
/**
303+
* The parent span identifier from {@link DatadogHttpCodec#SPAN_ID_KEY} header formatted as 16
304+
* hexadecimal characters, {@code null} if absent or invalid.
305+
*/
306+
private String datadogSpanIdHex;
272307

273308
public ExtractionCache(C carrier, AgentPropagation.ContextVisitor<C> getter) {
274309
this.keysAndValues = new ArrayList<>(32);
@@ -279,9 +314,24 @@ public ExtractionCache(C carrier, AgentPropagation.ContextVisitor<C> getter) {
279314
public boolean accept(String key, String value) {
280315
this.keysAndValues.add(key);
281316
this.keysAndValues.add(value);
317+
cacheDatadogSpanId(key, value);
282318
return true;
283319
}
284320

321+
private void cacheDatadogSpanId(String key, String value) {
322+
if (SPAN_ID_KEY.equalsIgnoreCase(key)) {
323+
try {
324+
// Parse numeric header value to format it as 16 hexadecimal character format
325+
this.datadogSpanIdHex = DDSpanId.toHexStringPadded(DDSpanId.from(value));
326+
} catch (NumberFormatException ignored) {
327+
}
328+
}
329+
}
330+
331+
private String getDatadogSpanIdHex() {
332+
return this.datadogSpanIdHex;
333+
}
334+
285335
@Override
286336
public void forEachKey(ExtractionCache<?> carrier, AgentPropagation.KeyClassifier classifier) {
287337
List<String> keysAndValues = carrier.keysAndValues;

dd-trace-core/src/main/java/datadog/trace/core/propagation/ptags/PTagsFactory.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,10 @@ static class PTags extends PropagationTags {
110110
*/
111111
protected volatile String error;
112112

113+
/**
114+
* The last parent span id using the 16-characters zero padded hexadecimal representation,
115+
* {@code null} if not set.
116+
*/
113117
private volatile CharSequence lastParentId;
114118

115119
public PTags(
@@ -271,7 +275,6 @@ public CharSequence getLastParentId() {
271275

272276
@Override
273277
public void updateLastParentId(CharSequence lastParentId) {
274-
lastParentId = "0000000000000000".equals(lastParentId) ? null : lastParentId;
275278
if (!Objects.equals(this.lastParentId, lastParentId)) {
276279
clearCachedHeader(W3C);
277280
this.lastParentId = TagValue.from(lastParentId);

dd-trace-core/src/main/java/datadog/trace/core/propagation/ptags/W3CPTagsCodec.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ public class W3CPTagsCodec extends PTagsCodec {
2626
private static final int MIN_ALLOWED_CHAR = 32;
2727
private static final int MAX_ALLOWED_CHAR = 126;
2828
private static final int MAX_MEMBER_COUNT = 32;
29-
private static final String LAST_PARENT_ZERO = "0000000000000000";
3029

3130
@Override
3231
PropagationTags fromHeaderValue(PTagsFactory tagsFactory, String value) {
@@ -96,7 +95,7 @@ PropagationTags fromHeaderValue(PTagsFactory tagsFactory, String value) {
9695
TagValue traceIdTagValue = null;
9796
boolean appsecPropagationEnabled = false;
9897
int maxUnknownSize = 0;
99-
CharSequence lastParentId = LAST_PARENT_ZERO;
98+
CharSequence lastParentId = null;
10099
while (tagPos < ddMemberValueEnd) {
101100
int tagKeyEndsAt =
102101
validateCharsUntilSeparatorOrEnd(
@@ -242,7 +241,7 @@ protected int appendPrefix(StringBuilder sb, PTags ptags) {
242241
}
243242
// append last ParentId (p)
244243
CharSequence lastParent = ptags.getLastParentId();
245-
if (lastParent != null && !lastParent.equals(LAST_PARENT_ZERO)) {
244+
if (lastParent != null) {
246245
if (sb.length() > EMPTY_SIZE) {
247246
sb.append(';');
248247
}

0 commit comments

Comments
 (0)