Skip to content

Commit 30eeaf5

Browse files
Add multiple context extraction support (DataDog#6138)
* feat(core): Add multiple context extraction Extract multiple propagated context according the new W3C trace context initiative. Add a config to limit to the first valid one. * fix(core): Fix B3 extractor tests * feat(core): Add terminated extracted contexts as span links * feat(core): Add parent span to tracestate * feat(core): Revert parent span addition to tracestate * feat(core): Clean up W3C propagation tags error handling * feat(core): Add tracestate propagation from different propagation tags * fix(core): Fix tracestate encoding when Datadog and other members are present using W3CPtags * fix(core): Fix terminated context addition in span builder * fix(core): Fix trace id comparison * chore: Clean up comments * feat(core): Ensure extracted context always have at least an empty PTags instance It will prevent the only case where a DDSpanContext can have a null PTags. This will allow to store tracestate if found from another valid extracted context.
1 parent 05ddf46 commit 30eeaf5

File tree

31 files changed

+482
-128
lines changed

31 files changed

+482
-128
lines changed

dd-java-agent/instrumentation/opentelemetry/opentelemetry-0.3/src/test/groovy/OpenTelemetryTest.groovy

+4-2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import datadog.trace.api.DDTags
44
import datadog.trace.api.DDTraceId
55
import datadog.trace.api.interceptor.MutableSpan
66
import datadog.trace.core.propagation.PropagationTags
7+
8+
import static datadog.trace.api.TracePropagationStyle.NONE
79
import static datadog.trace.api.sampling.PrioritySampling.*
810
import static datadog.trace.api.sampling.SamplingMechanism.*
911
import datadog.trace.context.TraceScope
@@ -136,11 +138,11 @@ class OpenTelemetryTest extends AgentTestRunner {
136138
setup:
137139
def builder = tracer.spanBuilder("some name")
138140
if (parentId) {
139-
def ctx = new ExtractedContext(DDTraceId.ONE, parentId, SAMPLER_DROP, null, PropagationTags.factory().empty())
141+
def ctx = new ExtractedContext(DDTraceId.ONE, parentId, SAMPLER_DROP, null, PropagationTags.factory().empty(), NONE)
140142
builder.setParent(tracer.converter.toSpanContext(ctx))
141143
}
142144
if (linkId) {
143-
def ctx = new ExtractedContext(DDTraceId.ONE, linkId, SAMPLER_DROP, null, PropagationTags.factory().empty())
145+
def ctx = new ExtractedContext(DDTraceId.ONE, linkId, SAMPLER_DROP, null, PropagationTags.factory().empty(), NONE)
144146
builder.addLink(tracer.converter.toSpanContext(ctx))
145147
}
146148
def result = builder.startSpan()

dd-java-agent/instrumentation/opentracing/api-0.31/src/test/groovy/OpenTracing31Test.groovy

+2-1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import datadog.trace.instrumentation.opentracing31.OTTracer
1010
import datadog.trace.instrumentation.opentracing31.TypeConverter
1111
import spock.lang.Shared
1212

13+
import static datadog.trace.api.TracePropagationStyle.NONE
1314
import static datadog.trace.api.sampling.PrioritySampling.*
1415
import static datadog.trace.api.sampling.SamplingMechanism.*
1516
import datadog.trace.context.TraceScope
@@ -45,7 +46,7 @@ class OpenTracing31Test extends AgentTestRunner {
4546
.withTag("boolean", true)
4647
}
4748
if (addReference) {
48-
def ctx = new ExtractedContext(DDTraceId.ONE, 2, SAMPLER_DROP, null, PropagationTags.factory().empty())
49+
def ctx = new ExtractedContext(DDTraceId.ONE, 2, SAMPLER_DROP, null, PropagationTags.factory().empty(), NONE)
4950
builder.addReference(addReference, tracer.tracer.converter.toSpanContext(ctx))
5051
}
5152
def result = builder.start()

dd-java-agent/instrumentation/opentracing/api-0.32/src/test/groovy/OpenTracing32Test.groovy

+2-1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import spock.lang.Shared
2323
import spock.lang.Subject
2424

2525
import static datadog.trace.agent.test.utils.TraceUtils.runUnderTrace
26+
import static datadog.trace.api.TracePropagationStyle.NONE
2627
import static datadog.trace.api.sampling.PrioritySampling.SAMPLER_DROP
2728
import static datadog.trace.api.sampling.PrioritySampling.SAMPLER_KEEP
2829
import static datadog.trace.api.sampling.PrioritySampling.UNSET
@@ -50,7 +51,7 @@ class OpenTracing32Test extends AgentTestRunner {
5051
.withTag("boolean", true)
5152
}
5253
if (addReference) {
53-
def ctx = new ExtractedContext(DDTraceId.ONE, 2, SAMPLER_DROP, null, PropagationTags.factory().empty())
54+
def ctx = new ExtractedContext(DDTraceId.ONE, 2, SAMPLER_DROP, null, PropagationTags.factory().empty(), NONE)
5455
builder.addReference(addReference, tracer.tracer.converter.toSpanContext(ctx))
5556
}
5657
def result = builder.start()

dd-trace-api/src/main/java/datadog/trace/api/ConfigDefaults.java

+2
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,8 @@ public final class ConfigDefaults {
7979

8080
static final int DEFAULT_CLOCK_SYNC_PERIOD = 30; // seconds
8181

82+
static final boolean DEFAULT_TRACE_PROPAGATION_EXTRACT_FIRST = false;
83+
8284
static final boolean DEFAULT_JMX_FETCH_MULTIPLE_RUNTIME_SERVICES_ENABLED = false;
8385
static final int DEFAULT_JMX_FETCH_MULTIPLE_RUNTIME_SERVICES_LIMIT = 10;
8486

dd-trace-api/src/main/java/datadog/trace/api/config/TracerConfig.java

+1
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ public final class TracerConfig {
8484
public static final String TRACE_PROPAGATION_STYLE = "trace.propagation.style";
8585
public static final String TRACE_PROPAGATION_STYLE_EXTRACT = "trace.propagation.style.extract";
8686
public static final String TRACE_PROPAGATION_STYLE_INJECT = "trace.propagation.style.inject";
87+
public static final String TRACE_PROPAGATION_EXTRACT_FIRST = "trace.propagation.extract.first";
8788

8889
public static final String ENABLE_TRACE_AGENT_V05 = "trace.agent.v0.5.enabled";
8990

dd-trace-core/src/main/java/datadog/trace/core/CoreTracer.java

+14
Original file line numberDiff line numberDiff line change
@@ -1191,6 +1191,7 @@ public CoreSpanBuilder ignoreActiveSpan() {
11911191
}
11921192

11931193
private DDSpan buildSpan() {
1194+
addTerminatedContextAsLinks();
11941195
DDSpan span = DDSpan.create(instrumentationName, timestampMicro, buildSpanContext(), links);
11951196
if (span.isLocalRootSpan()) {
11961197
EndpointTracker tracker = tracer.onRootSpanStarted(span);
@@ -1199,6 +1200,19 @@ private DDSpan buildSpan() {
11991200
return span;
12001201
}
12011202

1203+
private void addTerminatedContextAsLinks() {
1204+
if (this.parent instanceof TagContext) {
1205+
List<AgentSpanLink> terminatedContextLinks =
1206+
((TagContext) this.parent).getTerminatedContextLinks();
1207+
if (!terminatedContextLinks.isEmpty()) {
1208+
if (this.links == null) {
1209+
this.links = new ArrayList<>();
1210+
}
1211+
this.links.addAll(terminatedContextLinks);
1212+
}
1213+
}
1214+
}
1215+
12021216
@Override
12031217
public AgentSpan start() {
12041218
return buildSpan();

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

+14-1
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
package datadog.trace.core.propagation;
22

3+
import static datadog.trace.api.TracePropagationStyle.B3MULTI;
4+
import static datadog.trace.api.TracePropagationStyle.B3SINGLE;
35
import static datadog.trace.core.propagation.HttpCodec.firstHeaderValue;
46

57
import datadog.trace.api.Config;
68
import datadog.trace.api.DD128bTraceId;
79
import datadog.trace.api.DDSpanId;
810
import datadog.trace.api.DDTraceId;
911
import datadog.trace.api.TraceConfig;
12+
import datadog.trace.api.TracePropagationStyle;
1013
import datadog.trace.api.sampling.PrioritySampling;
1114
import datadog.trace.bootstrap.instrumentation.api.AgentPropagation;
1215
import datadog.trace.core.DDSpanContext;
@@ -161,7 +164,7 @@ static HttpCodec.Extractor newExtractor(
161164
final List<HttpCodec.Extractor> extractors = new ArrayList<>(2);
162165
extractors.add(newSingleExtractor(config, traceConfigSupplier));
163166
extractors.add(newMultiExtractor(config, traceConfigSupplier));
164-
return new HttpCodec.CompoundExtractor(extractors);
167+
return new HttpCodec.CompoundExtractor(extractors, config.isTracePropagationExtractFirst());
165168
}
166169

167170
public static HttpCodec.Extractor newMultiExtractor(
@@ -212,6 +215,11 @@ private B3MultiContextInterpreter(Config config) {
212215
super(config);
213216
}
214217

218+
@Override
219+
public TracePropagationStyle style() {
220+
return B3MULTI;
221+
}
222+
215223
@Override
216224
public boolean accept(final String key, final String value) {
217225
if (null == key || key.isEmpty() || null == value || value.isEmpty()) {
@@ -267,6 +275,11 @@ public B3SingleContextInterpreter(Config config) {
267275
super(config);
268276
}
269277

278+
@Override
279+
public TracePropagationStyle style() {
280+
return B3SINGLE;
281+
}
282+
270283
@Override
271284
public boolean accept(String key, String value) {
272285
try {

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

+31-19
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import datadog.trace.api.DDTraceId;
2222
import datadog.trace.api.Functions;
2323
import datadog.trace.api.TraceConfig;
24+
import datadog.trace.api.TracePropagationStyle;
2425
import datadog.trace.api.cache.DDCache;
2526
import datadog.trace.api.cache.DDCaches;
2627
import datadog.trace.api.sampling.PrioritySampling;
@@ -47,6 +48,7 @@ public abstract class ContextInterpreter implements AgentPropagation.KeyClassifi
4748
protected long endToEndStartTime;
4849
protected boolean valid;
4950
protected boolean fullContext;
51+
protected final PropagationTags.Factory propagationTagsFactory;
5052
protected PropagationTags propagationTags;
5153

5254
private TagContext.HttpHeaders httpHeaders;
@@ -58,19 +60,23 @@ public abstract class ContextInterpreter implements AgentPropagation.KeyClassifi
5860
protected static final boolean LOG_EXTRACT_HEADER_NAMES = Config.get().isLogExtractHeaderNames();
5961
private static final DDCache<String, String> CACHE = DDCaches.newFixedSizeCache(64);
6062

61-
protected String toLowerCase(String key) {
63+
protected static String toLowerCase(String key) {
6264
return CACHE.computeIfAbsent(key, Functions.LowerCase.INSTANCE);
6365
}
6466

6567
protected ContextInterpreter(Config config) {
6668
this.customIpHeaderName = config.getTraceClientIpHeader();
6769
this.clientIpResolutionEnabled = config.isTraceClientIpResolverEnabled();
6870
this.clientIpWithoutAppSec = config.isClientIpEnabled();
71+
this.propagationTagsFactory = PropagationTags.factory(config);
6972
}
7073

71-
public interface Factory {
72-
ContextInterpreter create();
73-
}
74+
/**
75+
* Gets the propagation style handled by the context interpreter.
76+
*
77+
* @return The propagation style handled.
78+
*/
79+
public abstract TracePropagationStyle style();
7480

7581
protected final boolean handledForwarding(String key, String value) {
7682
if (value == null || !collectIpHeaders) {
@@ -229,20 +235,21 @@ public ContextInterpreter reset(TraceConfig traceConfig) {
229235
protected TagContext build() {
230236
if (valid) {
231237
if (fullContext && !DDTraceId.ZERO.equals(traceId)) {
232-
final ExtractedContext context;
233-
context =
234-
new ExtractedContext(
235-
traceId,
236-
spanId,
237-
samplingPriorityOrDefault(traceId, samplingPriority),
238-
origin,
239-
endToEndStartTime,
240-
baggage,
241-
tags,
242-
httpHeaders,
243-
propagationTags,
244-
traceConfig);
245-
return context;
238+
if (propagationTags == null) {
239+
propagationTags = propagationTagsFactory.empty();
240+
}
241+
return new ExtractedContext(
242+
traceId,
243+
spanId,
244+
samplingPriorityOrDefault(traceId, samplingPriority),
245+
origin,
246+
endToEndStartTime,
247+
baggage,
248+
tags,
249+
httpHeaders,
250+
propagationTags,
251+
traceConfig,
252+
style());
246253
} else if (origin != null
247254
|| !tags.isEmpty()
248255
|| httpHeaders != null
@@ -254,7 +261,8 @@ protected TagContext build() {
254261
httpHeaders,
255262
baggage,
256263
samplingPriorityOrDefault(traceId, samplingPriority),
257-
traceConfig);
264+
traceConfig,
265+
style());
258266
}
259267
}
260268
return null;
@@ -284,4 +292,8 @@ private int samplingPriorityOrDefault(DDTraceId traceId, int samplingPriority) {
284292
? defaultSamplingPriority()
285293
: samplingPriority;
286294
}
295+
296+
public interface Factory {
297+
ContextInterpreter create();
298+
}
287299
}

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

+10-6
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package datadog.trace.core.propagation;
22

3+
import static datadog.trace.api.TracePropagationStyle.DATADOG;
34
import static datadog.trace.core.propagation.HttpCodec.firstHeaderValue;
45
import static datadog.trace.core.propagation.XRayHttpCodec.XRayContextInterpreter.handleXRayTraceHeader;
56
import static datadog.trace.core.propagation.XRayHttpCodec.X_AMZN_TRACE_ID;
@@ -12,9 +13,11 @@
1213
import datadog.trace.api.DDTags;
1314
import datadog.trace.api.DDTraceId;
1415
import datadog.trace.api.TraceConfig;
16+
import datadog.trace.api.TracePropagationStyle;
1517
import datadog.trace.bootstrap.instrumentation.api.AgentPropagation;
1618
import datadog.trace.bootstrap.instrumentation.api.TagContext;
1719
import datadog.trace.core.DDSpanContext;
20+
import datadog.trace.core.propagation.PropagationTags.HeaderType;
1821
import java.util.Map;
1922
import java.util.TreeMap;
2023
import java.util.function.Supplier;
@@ -75,8 +78,7 @@ public <C> void inject(
7578
}
7679

7780
// inject x-datadog-tags
78-
String datadogTags =
79-
context.getPropagationTags().headerValue(PropagationTags.HeaderType.DATADOG);
81+
String datadogTags = context.getPropagationTags().headerValue(HeaderType.DATADOG);
8082
if (datadogTags != null) {
8183
setter.set(carrier, DATADOG_TAGS_KEY, datadogTags);
8284
}
@@ -101,12 +103,15 @@ private static class DatadogContextInterpreter extends ContextInterpreter {
101103
private static final int IGNORE = -1;
102104

103105
private final boolean isAwsPropagationEnabled;
104-
private final PropagationTags.Factory datadogTagsFactory;
105106

106107
private DatadogContextInterpreter(Config config) {
107108
super(config);
108109
isAwsPropagationEnabled = config.isAwsPropagationEnabled();
109-
datadogTagsFactory = PropagationTags.factory(config);
110+
}
111+
112+
@Override
113+
public TracePropagationStyle style() {
114+
return DATADOG;
110115
}
111116

112117
@Override
@@ -180,8 +185,7 @@ public boolean accept(String key, String value) {
180185
endToEndStartTime = extractEndToEndStartTime(firstHeaderValue(value));
181186
break;
182187
case DD_TAGS:
183-
propagationTags =
184-
datadogTagsFactory.fromHeaderValue(PropagationTags.HeaderType.DATADOG, value);
188+
propagationTags = propagationTagsFactory.fromHeaderValue(HeaderType.DATADOG, value);
185189
break;
186190
case OT_BAGGAGE:
187191
{

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

+18-4
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import datadog.trace.api.DDTraceId;
44
import datadog.trace.api.TraceConfig;
5+
import datadog.trace.api.TracePropagationStyle;
56
import datadog.trace.api.sampling.PrioritySampling;
67
import datadog.trace.bootstrap.instrumentation.api.TagContext;
78
import java.util.Map;
@@ -20,8 +21,20 @@ public ExtractedContext(
2021
final long spanId,
2122
final int samplingPriority,
2223
final CharSequence origin,
23-
final PropagationTags propagationTags) {
24-
this(traceId, spanId, samplingPriority, origin, 0, null, null, null, propagationTags, null);
24+
final PropagationTags propagationTags,
25+
final TracePropagationStyle propagationStyle) {
26+
this(
27+
traceId,
28+
spanId,
29+
samplingPriority,
30+
origin,
31+
0,
32+
null,
33+
null,
34+
null,
35+
propagationTags,
36+
null,
37+
propagationStyle);
2538
}
2639

2740
public ExtractedContext(
@@ -34,8 +47,9 @@ public ExtractedContext(
3447
final Map<String, String> tags,
3548
final HttpHeaders httpHeaders,
3649
final PropagationTags propagationTags,
37-
final TraceConfig traceConfig) {
38-
super(origin, tags, httpHeaders, baggage, samplingPriority, traceConfig);
50+
final TraceConfig traceConfig,
51+
final TracePropagationStyle propagationStyle) {
52+
super(origin, tags, httpHeaders, baggage, samplingPriority, traceConfig, propagationStyle);
3953
this.traceId = traceId;
4054
this.spanId = spanId;
4155
this.endToEndStartTime = endToEndStartTime;

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

+7
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
package datadog.trace.core.propagation;
22

3+
import static datadog.trace.api.TracePropagationStyle.HAYSTACK;
34
import static datadog.trace.core.propagation.HttpCodec.firstHeaderValue;
45

56
import datadog.trace.api.Config;
67
import datadog.trace.api.DD64bTraceId;
78
import datadog.trace.api.DDSpanId;
89
import datadog.trace.api.DDTraceId;
910
import datadog.trace.api.TraceConfig;
11+
import datadog.trace.api.TracePropagationStyle;
1012
import datadog.trace.api.sampling.PrioritySampling;
1113
import datadog.trace.bootstrap.instrumentation.api.AgentPropagation;
1214
import datadog.trace.core.DDSpanContext;
@@ -139,6 +141,11 @@ private HaystackContextInterpreter(Config config) {
139141
super(config);
140142
}
141143

144+
@Override
145+
public TracePropagationStyle style() {
146+
return HAYSTACK;
147+
}
148+
142149
@Override
143150
public boolean accept(String key, String value) {
144151
if (null == key || key.isEmpty()) {

0 commit comments

Comments
 (0)