diff --git a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/context/OtelContext.java b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/context/OtelContext.java index 0f0bded653b..8761e1db7e0 100644 --- a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/context/OtelContext.java +++ b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/context/OtelContext.java @@ -20,6 +20,7 @@ public class OtelContext implements Context { private static final String OTEL_CONTEXT_SPAN_KEY = "opentelemetry-trace-span-key"; private static final String OTEL_CONTEXT_ROOT_SPAN_KEY = "opentelemetry-traces-local-root-span"; + private static final String OTEL_CONTEXT_BAGGAGE_KEY = "opentelemetry-baggage-key"; /** Records the keys needed to access the delegate context, mapped by key name. */ private static final Map, datadog.context.ContextKey> DELEGATE_KEYS = @@ -84,6 +85,10 @@ public int hashCode() { return delegate.hashCode(); } + public static String getOtelContextBaggageKey() { + return OTEL_CONTEXT_BAGGAGE_KEY; + } + @Override public String toString() { return "OtelContext{" + "delegate=" + delegate + '}'; diff --git a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/context/propagation/AgentTextMapPropagator.java b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/context/propagation/AgentTextMapPropagator.java index ae7d708b5e4..1060b037c87 100644 --- a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/context/propagation/AgentTextMapPropagator.java +++ b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/context/propagation/AgentTextMapPropagator.java @@ -9,10 +9,12 @@ import datadog.trace.bootstrap.instrumentation.api.AgentSpan; import datadog.trace.bootstrap.instrumentation.api.AgentSpanContext; import datadog.trace.bootstrap.instrumentation.api.AgentSpanContext.Extracted; +import datadog.trace.bootstrap.instrumentation.api.BaggageContext; import datadog.trace.bootstrap.instrumentation.api.TagContext; import datadog.trace.util.PropagationUtils; import io.opentelemetry.api.trace.TraceState; import io.opentelemetry.context.Context; +import io.opentelemetry.context.ContextKey; import io.opentelemetry.context.propagation.TextMapGetter; import io.opentelemetry.context.propagation.TextMapPropagator; import io.opentelemetry.context.propagation.TextMapSetter; @@ -61,7 +63,12 @@ private static datadog.context.Context convertContext(Context context) { } // Otherwise, fallback to extracting limited tracing context and recreating an OTel context from AgentSpanContext extract = OtelExtractedContext.extract(context); - return AgentSpan.fromSpanContext(extract); + AgentSpan agentSpan = AgentSpan.fromSpanContext(extract); + datadog.context.Context c = + agentSpan.with( + BaggageContext.getContextKey(), + context.get(ContextKey.named(OtelContext.getOtelContextBaggageKey()))); + return c; } /** diff --git a/dd-java-agent/instrumentation/akka-http/akka-http-10.6/src/main/java11/datadog/trace/instrumentation/akkahttp106/SingleRequestAdvice.java b/dd-java-agent/instrumentation/akka-http/akka-http-10.6/src/main/java11/datadog/trace/instrumentation/akkahttp106/SingleRequestAdvice.java index 105ac824f93..87b490ff882 100644 --- a/dd-java-agent/instrumentation/akka-http/akka-http-10.6/src/main/java11/datadog/trace/instrumentation/akkahttp106/SingleRequestAdvice.java +++ b/dd-java-agent/instrumentation/akka-http/akka-http-10.6/src/main/java11/datadog/trace/instrumentation/akkahttp106/SingleRequestAdvice.java @@ -8,6 +8,7 @@ import akka.http.scaladsl.HttpExt; import akka.http.scaladsl.model.HttpRequest; import akka.http.scaladsl.model.HttpResponse; +import datadog.context.Context; import datadog.trace.api.datastreams.DataStreamsContext; import datadog.trace.bootstrap.instrumentation.api.AgentScope; import datadog.trace.bootstrap.instrumentation.api.AgentSpan; @@ -30,7 +31,7 @@ public static AgentScope methodEnter( if (request != null) { DataStreamsContext dsmContext = DataStreamsContext.fromTags(CLIENT_PATHWAY_EDGE_TAGS); - defaultPropagator().inject(span.with(dsmContext), request, headers); + defaultPropagator().inject(Context.current().with(span).with(dsmContext), request, headers); // Request is immutable, so we have to assign new value once we update headers request = headers.getRequest(); } diff --git a/dd-java-agent/instrumentation/opentelemetry/opentelemetry-1.4/src/test/groovy/opentelemetry14/context/propagation/BaggagePropagatorTest.groovy b/dd-java-agent/instrumentation/opentelemetry/opentelemetry-1.4/src/test/groovy/opentelemetry14/context/propagation/BaggagePropagatorTest.groovy new file mode 100644 index 00000000000..e62d05aa10f --- /dev/null +++ b/dd-java-agent/instrumentation/opentelemetry/opentelemetry-1.4/src/test/groovy/opentelemetry14/context/propagation/BaggagePropagatorTest.groovy @@ -0,0 +1,39 @@ +package opentelemetry14.context.propagation + + +import datadog.trace.api.DDTraceId + +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 +import static java.lang.Long.parseLong + +class BaggagePropagatorTest extends AgentPropagatorTest { + @Override + String style() { + return 'baggage' + } + + def values() { + // spotless:off + return [ + [["baggage": "key1=val1,key2=val2,foo=bar"], '00000000000000001111111111111111', '2222222222222222', UNSET], + ] + // spotless:on + } + + void assertInjectedHeaders(Map headers, String traceId, String spanId, byte sampling) { + assert headers['x-datadog-trace-id'] == Long.toString(DDTraceId.fromHex(traceId).toLong()) + assert headers['x-datadog-parent-id'] == spanId.replaceAll('^0+(?!$)', '') + def tags = [] + def samplingPriority = sampling == SAMPLER_DROP ? '0' : '1' // Deterministic sampler with rate to 1 if not explicitly dropped + if (sampling == UNSET) { + tags+= '_dd.p.dm=-1' + } + if (traceId.length() == 32) { + tags+= '_dd.p.tid='+ traceId.substring(0, 16) + } + assert headers['x-datadog-tags'] == tags.join(',') + assert headers['x-datadog-sampling-priority'] == samplingPriority + } +} diff --git a/internal-api/src/main/java/datadog/trace/bootstrap/instrumentation/api/BaggageContext.java b/internal-api/src/main/java/datadog/trace/bootstrap/instrumentation/api/BaggageContext.java new file mode 100644 index 00000000000..5870d205b78 --- /dev/null +++ b/internal-api/src/main/java/datadog/trace/bootstrap/instrumentation/api/BaggageContext.java @@ -0,0 +1,78 @@ +package datadog.trace.bootstrap.instrumentation.api; + +import datadog.context.Context; +import datadog.context.ContextKey; +import datadog.context.ImplicitContextKeyed; +import java.util.HashMap; +import java.util.Map; + +public class BaggageContext implements ImplicitContextKeyed { + private static final ContextKey CONTEXT_KEY = ContextKey.named("baggage-key"); + + private final Map baggage; + private String baggageString; + private boolean updatedCache; + + public BaggageContext empty() { + return create(new HashMap<>(), ""); + } + + public static BaggageContext create(Map baggage) { + return new BaggageContext(baggage); + } + + private BaggageContext(Map baggage) { + this.baggage = baggage; + this.baggageString = ""; + updatedCache = false; + } + + public static BaggageContext create(Map baggage, String w3cHeader) { + return new BaggageContext(baggage, w3cHeader); + } + + private BaggageContext(Map baggage, String baggageString) { + this.baggage = baggage; + this.baggageString = baggageString; + updatedCache = true; + } + + public void addW3CBaggage(String key, String value) { + baggage.put(key, value); + updatedCache = false; + } + + public void removeW3CBaggage(String key) { + baggage.remove(key); + updatedCache = false; + } + + public void setW3cBaggageHeader(String w3cHeader) { + this.baggageString = w3cHeader; + updatedCache = true; + } + + public String getW3cBaggageHeader() { + if (updatedCache) { + return baggageString; + } + return null; + } + + public Map asMap() { + return new HashMap<>(baggage); + } + + public static ContextKey getContextKey() { + return CONTEXT_KEY; + } + + public static BaggageContext fromContext(Context context) { + return context.get(CONTEXT_KEY); + } + + @Override + public Context storeInto(Context context) { + return context.with(CONTEXT_KEY, this); + } +}