Skip to content

Commit

Permalink
feat(core): Add multiple context extraction
Browse files Browse the repository at this point in the history
Extract multiple propagated context according the new W3C trace context initiative.
Add a config to limit to the first valid one.
  • Loading branch information
PerfectSlayer committed Nov 2, 2023
1 parent 20a70a4 commit 363b40a
Show file tree
Hide file tree
Showing 22 changed files with 205 additions and 48 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import datadog.trace.api.DDTags
import datadog.trace.api.DDTraceId
import datadog.trace.api.interceptor.MutableSpan
import datadog.trace.core.propagation.PropagationTags

import static datadog.trace.api.TracePropagationStyle.NONE
import static datadog.trace.api.sampling.PrioritySampling.*
import static datadog.trace.api.sampling.SamplingMechanism.*
import datadog.trace.context.TraceScope
Expand Down Expand Up @@ -135,11 +137,11 @@ class OpenTelemetryTest extends AgentTestRunner {
setup:
def builder = tracer.spanBuilder("some name")
if (parentId) {
def ctx = new ExtractedContext(DDTraceId.ONE, parentId, SAMPLER_DROP, null, PropagationTags.factory().empty())
def ctx = new ExtractedContext(DDTraceId.ONE, parentId, SAMPLER_DROP, null, PropagationTags.factory().empty(), NONE)
builder.setParent(tracer.converter.toSpanContext(ctx))
}
if (linkId) {
def ctx = new ExtractedContext(DDTraceId.ONE, linkId, SAMPLER_DROP, null, PropagationTags.factory().empty())
def ctx = new ExtractedContext(DDTraceId.ONE, linkId, SAMPLER_DROP, null, PropagationTags.factory().empty(), NONE)
builder.addLink(tracer.converter.toSpanContext(ctx))
}
def result = builder.startSpan()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import datadog.trace.instrumentation.opentracing31.OTTracer
import datadog.trace.instrumentation.opentracing31.TypeConverter
import spock.lang.Shared

import static datadog.trace.api.TracePropagationStyle.NONE
import static datadog.trace.api.sampling.PrioritySampling.*
import static datadog.trace.api.sampling.SamplingMechanism.*
import datadog.trace.context.TraceScope
Expand Down Expand Up @@ -45,7 +46,7 @@ class OpenTracing31Test extends AgentTestRunner {
.withTag("boolean", true)
}
if (addReference) {
def ctx = new ExtractedContext(DDTraceId.ONE, 2, SAMPLER_DROP, null, PropagationTags.factory().empty())
def ctx = new ExtractedContext(DDTraceId.ONE, 2, SAMPLER_DROP, null, PropagationTags.factory().empty(), NONE)
builder.addReference(addReference, tracer.tracer.converter.toSpanContext(ctx))
}
def result = builder.start()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import spock.lang.Shared
import spock.lang.Subject

import static datadog.trace.agent.test.utils.TraceUtils.runUnderTrace
import static datadog.trace.api.TracePropagationStyle.NONE
import static datadog.trace.api.sampling.PrioritySampling.SAMPLER_DROP
import static datadog.trace.api.sampling.PrioritySampling.SAMPLER_KEEP
import static datadog.trace.api.sampling.PrioritySampling.UNSET
Expand Down Expand Up @@ -50,7 +51,7 @@ class OpenTracing32Test extends AgentTestRunner {
.withTag("boolean", true)
}
if (addReference) {
def ctx = new ExtractedContext(DDTraceId.ONE, 2, SAMPLER_DROP, null, PropagationTags.factory().empty())
def ctx = new ExtractedContext(DDTraceId.ONE, 2, SAMPLER_DROP, null, PropagationTags.factory().empty(), NONE)
builder.addReference(addReference, tracer.tracer.converter.toSpanContext(ctx))
}
def result = builder.start()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ public final class ConfigDefaults {

static final int DEFAULT_CLOCK_SYNC_PERIOD = 30; // seconds

static final boolean DEFAULT_TRACE_PROPAGATION_EXTRACT_FIRST = false;

static final boolean DEFAULT_JMX_FETCH_MULTIPLE_RUNTIME_SERVICES_ENABLED = false;
static final int DEFAULT_JMX_FETCH_MULTIPLE_RUNTIME_SERVICES_LIMIT = 10;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ public final class TracerConfig {
public static final String TRACE_PROPAGATION_STYLE = "trace.propagation.style";
public static final String TRACE_PROPAGATION_STYLE_EXTRACT = "trace.propagation.style.extract";
public static final String TRACE_PROPAGATION_STYLE_INJECT = "trace.propagation.style.inject";
public static final String TRACE_PROPAGATION_EXTRACT_FIRST = "trace.propagation.extract.first";

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

Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
package datadog.trace.core.propagation;

import static datadog.trace.api.TracePropagationStyle.B3MULTI;
import static datadog.trace.api.TracePropagationStyle.B3SINGLE;
import static datadog.trace.core.propagation.HttpCodec.firstHeaderValue;

import datadog.trace.api.Config;
import datadog.trace.api.DD128bTraceId;
import datadog.trace.api.DDSpanId;
import datadog.trace.api.DDTraceId;
import datadog.trace.api.TraceConfig;
import datadog.trace.api.TracePropagationStyle;
import datadog.trace.api.sampling.PrioritySampling;
import datadog.trace.bootstrap.instrumentation.api.AgentPropagation;
import datadog.trace.core.DDSpanContext;
Expand Down Expand Up @@ -161,7 +164,7 @@ static HttpCodec.Extractor newExtractor(
final List<HttpCodec.Extractor> extractors = new ArrayList<>(2);
extractors.add(newSingleExtractor(config, traceConfigSupplier));
extractors.add(newMultiExtractor(config, traceConfigSupplier));
return new HttpCodec.CompoundExtractor(extractors);
return new HttpCodec.CompoundExtractor(extractors, config.isTracePropagationExtractFirst());
}

public static HttpCodec.Extractor newMultiExtractor(
Expand Down Expand Up @@ -212,6 +215,11 @@ private B3MultiContextInterpreter(Config config) {
super(config);
}

@Override
public TracePropagationStyle style() {
return B3MULTI;
}

@Override
public boolean accept(final String key, final String value) {
if (null == key || key.isEmpty() || null == value || value.isEmpty()) {
Expand Down Expand Up @@ -267,6 +275,11 @@ public B3SingleContextInterpreter(Config config) {
super(config);
}

@Override
public TracePropagationStyle style() {
return B3SINGLE;
}

@Override
public boolean accept(String key, String value) {
try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import datadog.trace.api.DDTraceId;
import datadog.trace.api.Functions;
import datadog.trace.api.TraceConfig;
import datadog.trace.api.TracePropagationStyle;
import datadog.trace.api.cache.DDCache;
import datadog.trace.api.cache.DDCaches;
import datadog.trace.api.sampling.PrioritySampling;
Expand Down Expand Up @@ -58,7 +59,7 @@ public abstract class ContextInterpreter implements AgentPropagation.KeyClassifi
protected static final boolean LOG_EXTRACT_HEADER_NAMES = Config.get().isLogExtractHeaderNames();
private static final DDCache<String, String> CACHE = DDCaches.newFixedSizeCache(64);

protected String toLowerCase(String key) {
protected static String toLowerCase(String key) {
return CACHE.computeIfAbsent(key, Functions.LowerCase.INSTANCE);
}

Expand All @@ -68,9 +69,12 @@ protected ContextInterpreter(Config config) {
this.clientIpWithoutAppSec = config.isClientIpEnabled();
}

public interface Factory {
ContextInterpreter create();
}
/**
* Gets the propagation style handled by the context interpreter.
*
* @return The propagation style handled.
*/
public abstract TracePropagationStyle style();

protected final boolean handledForwarding(String key, String value) {
if (value == null || !collectIpHeaders) {
Expand Down Expand Up @@ -241,7 +245,8 @@ protected TagContext build() {
tags,
httpHeaders,
propagationTags,
traceConfig);
traceConfig,
style());
return context;
} else if (origin != null
|| !tags.isEmpty()
Expand All @@ -254,7 +259,8 @@ protected TagContext build() {
httpHeaders,
baggage,
samplingPriorityOrDefault(traceId, samplingPriority),
traceConfig);
traceConfig,
style());
}
}
return null;
Expand Down Expand Up @@ -284,4 +290,8 @@ private int samplingPriorityOrDefault(DDTraceId traceId, int samplingPriority) {
? defaultSamplingPriority()
: samplingPriority;
}

public interface Factory {
ContextInterpreter create();
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package datadog.trace.core.propagation;

import static datadog.trace.api.TracePropagationStyle.DATADOG;
import static datadog.trace.core.propagation.HttpCodec.firstHeaderValue;
import static datadog.trace.core.propagation.XRayHttpCodec.XRayContextInterpreter.handleXRayTraceHeader;
import static datadog.trace.core.propagation.XRayHttpCodec.X_AMZN_TRACE_ID;
Expand All @@ -12,6 +13,7 @@
import datadog.trace.api.DDTags;
import datadog.trace.api.DDTraceId;
import datadog.trace.api.TraceConfig;
import datadog.trace.api.TracePropagationStyle;
import datadog.trace.bootstrap.instrumentation.api.AgentPropagation;
import datadog.trace.bootstrap.instrumentation.api.TagContext;
import datadog.trace.core.DDSpanContext;
Expand Down Expand Up @@ -109,6 +111,11 @@ private DatadogContextInterpreter(Config config) {
datadogTagsFactory = PropagationTags.factory(config);
}

@Override
public TracePropagationStyle style() {
return DATADOG;
}

@Override
public boolean accept(String key, String value) {
if (null == key || key.isEmpty()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import datadog.trace.api.DDTraceId;
import datadog.trace.api.TraceConfig;
import datadog.trace.api.TracePropagationStyle;
import datadog.trace.api.sampling.PrioritySampling;
import datadog.trace.bootstrap.instrumentation.api.TagContext;
import java.util.Map;
Expand All @@ -20,8 +21,20 @@ public ExtractedContext(
final long spanId,
final int samplingPriority,
final CharSequence origin,
final PropagationTags propagationTags) {
this(traceId, spanId, samplingPriority, origin, 0, null, null, null, propagationTags, null);
final PropagationTags propagationTags,
final TracePropagationStyle propagationStyle) {
this(
traceId,
spanId,
samplingPriority,
origin,
0,
null,
null,
null,
propagationTags,
null,
propagationStyle);
}

public ExtractedContext(
Expand All @@ -34,8 +47,9 @@ public ExtractedContext(
final Map<String, String> tags,
final HttpHeaders httpHeaders,
final PropagationTags propagationTags,
final TraceConfig traceConfig) {
super(origin, tags, httpHeaders, baggage, samplingPriority, traceConfig);
final TraceConfig traceConfig,
final TracePropagationStyle propagationStyle) {
super(origin, tags, httpHeaders, baggage, samplingPriority, traceConfig, propagationStyle);
this.traceId = traceId;
this.spanId = spanId;
this.endToEndStartTime = endToEndStartTime;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
package datadog.trace.core.propagation;

import static datadog.trace.api.TracePropagationStyle.HAYSTACK;
import static datadog.trace.core.propagation.HttpCodec.firstHeaderValue;

import datadog.trace.api.Config;
import datadog.trace.api.DD64bTraceId;
import datadog.trace.api.DDSpanId;
import datadog.trace.api.DDTraceId;
import datadog.trace.api.TraceConfig;
import datadog.trace.api.TracePropagationStyle;
import datadog.trace.api.sampling.PrioritySampling;
import datadog.trace.bootstrap.instrumentation.api.AgentPropagation;
import datadog.trace.core.DDSpanContext;
Expand Down Expand Up @@ -139,6 +141,11 @@ private HaystackContextInterpreter(Config config) {
super(config);
}

@Override
public TracePropagationStyle style() {
return HAYSTACK;
}

@Override
public boolean accept(String key, String value) {
if (null == key || key.isEmpty()) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package datadog.trace.core.propagation;

import static datadog.trace.api.TracePropagationStyle.TRACECONTEXT;

import datadog.trace.api.Config;
import datadog.trace.api.TraceConfig;
import datadog.trace.api.TracePropagationStyle;
Expand Down Expand Up @@ -46,7 +48,17 @@ <C> void inject(
final DDSpanContext context, final C carrier, final AgentPropagation.Setter<C> setter);
}

/** This interface defines propagated context extractor. */
public interface Extractor {
/**
* Extracts a propagated context from the given carrier using the provided getter.
*
* @param carrier The carrier containing the propagated context.
* @param getter The getter used to extract data from the carrier.
* @param <C> The type of the carrier.
* @return {@code null} for failed context extraction, a {@link TagContext} instance for partial
* context extraction or an {@link ExtractedContext} for complete context extraction.
*/
<C> TagContext extract(final C carrier, final AgentPropagation.ContextVisitor<C> getter);
}

Expand Down Expand Up @@ -136,7 +148,7 @@ public static Extractor createExtractor(
break;
}
}
return new CompoundExtractor(extractors);
return new CompoundExtractor(extractors, config.isTracePropagationExtractFirst());
}

public static class CompoundInjector implements Injector {
Expand All @@ -159,27 +171,58 @@ public <C> void inject(

public static class CompoundExtractor implements Extractor {
private final List<Extractor> extractors;
private final boolean extractFirst;

public CompoundExtractor(final List<Extractor> extractors) {
public CompoundExtractor(final List<Extractor> extractors, boolean extractFirst) {
this.extractors = extractors;
this.extractFirst = extractFirst;
}

@Override
public <C> TagContext extract(
final C carrier, final AgentPropagation.ContextVisitor<C> getter) {
TagContext context = null;

for (final Extractor extractor : extractors) {
context = extractor.extract(carrier, getter);
// Use incomplete TagContext only as last resort
if (context instanceof ExtractedContext) {
log.debug("Extract complete context {}", context);
return context;
ExtractedContext context = null;
TagContext partialContext = null;

for (final Extractor extractor : this.extractors) {
TagContext extracted = extractor.extract(carrier, getter);
// Check if context is valid
if (extracted instanceof ExtractedContext) {
// If no prior valid context, store it as first valid context
if (context == null) {
context = (ExtractedContext) extracted;
// Stop extraction if only extracting first valid context and drop everything else
if (this.extractFirst) {
break;
}
}
// If another valid context is extracted
else {
boolean comingFromTraceContext = extracted.getPropagationStyle() == TRACECONTEXT;
boolean traceIdMatches = context.getTraceId().equals(extracted.getTraceId());
if (comingFromTraceContext && traceIdMatches) {
// Propagate newly extracted W3C tracestate to first valid context
// TODO
// context.
}
}
}
// Check if context is at least partial to keep it as first valid partial context found
else if (extracted != null && partialContext == null) {
partialContext = extracted;
}
}

log.debug("Extract incomplete context {}", context);
return context;
if (context != null) {
log.debug("Extract complete context {}", context);
return context;
} else if (partialContext != null) {
log.debug("Extract incomplete context {}", partialContext);
return partialContext;
} else {
log.debug("Extract no context");
return null;
}
}
}

Expand Down
Loading

0 comments on commit 363b40a

Please sign in to comment.