Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,10 @@
/internal-api/src/test/groovy/datadog/trace/api/sampling @DataDog/apm-sdk-api-java

# @DataDog/apm-serverless
/dd-trace-core/src/main/java/datadog/trace/lambda/ @DataDog/apm-serverless
/dd-trace-core/src/test/groovy/datadog/trace/lambda/ @DataDog/apm-serverless
/dd-trace-core/src/main/java/datadog/trace/lambda/ @DataDog/apm-serverless
/dd-trace-core/src/test/groovy/datadog/trace/lambda/ @DataDog/apm-serverless
**/InferredProxy*.java @DataDog/apm-serverless
**/InferredProxy*.groovy @DataDog/apm-serverless

# @DataDog/apm-lang-platform-java
/.circleci/ @DataDog/apm-lang-platform-java
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import datadog.trace.api.gateway.Flow;
import datadog.trace.api.gateway.Flow.Action.RequestBlockingAction;
import datadog.trace.api.gateway.IGSpanInfo;
import datadog.trace.api.gateway.InferredProxySpan;
import datadog.trace.api.gateway.RequestContext;
import datadog.trace.api.gateway.RequestContextSlot;
import datadog.trace.api.naming.SpanNaming;
Expand Down Expand Up @@ -147,20 +148,32 @@ public Context startSpan(REQUEST_CARRIER carrier, Context context) {
instrumentationNames != null && instrumentationNames.length > 0
? instrumentationNames[0]
: DEFAULT_INSTRUMENTATION_NAME;
AgentSpanContext.Extracted extracted = callIGCallbackStart(getExtractedSpanContext(context));
AgentSpanContext extracted = getExtractedSpanContext(context);
// Call IG callbacks
extracted = callIGCallbackStart(extracted);
// Create gateway inferred span if needed
extracted = startInferredProxySpan(context, extracted);
AgentSpan span =
tracer().startSpan(instrumentationName, spanName(), extracted).setMeasured(true);
// Apply RequestBlockingAction if any
Flow<Void> flow = callIGCallbackRequestHeaders(span, carrier);
if (flow.getAction() instanceof RequestBlockingAction) {
span.setRequestBlockingAction((RequestBlockingAction) flow.getAction());
}
AgentPropagation.ContextVisitor<REQUEST_CARRIER> getter = getter();
if (null != carrier && null != getter) {
tracer().getDataStreamsMonitoring().setCheckpoint(span, forHttpServer());
}
// DSM Checkpoint
tracer().getDataStreamsMonitoring().setCheckpoint(span, forHttpServer());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this change related to inferred span?

return context.with(span);
}

protected AgentSpanContext startInferredProxySpan(Context context, AgentSpanContext extracted) {
InferredProxySpan span;
if (!Config.get().isInferredProxyPropagationEnabled()
|| (span = InferredProxySpan.fromContext(context)) == null) {
return extracted;
}
return span.start(extracted);
}

public AgentSpan onRequest(
final AgentSpan span,
final CONNECTION connection,
Expand Down Expand Up @@ -381,8 +394,7 @@ public AgentSpan onResponse(final AgentSpan span, final RESPONSE response) {
return span;
}

private AgentSpanContext.Extracted callIGCallbackStart(
@Nullable final AgentSpanContext.Extracted extracted) {
private AgentSpanContext callIGCallbackStart(@Nullable final AgentSpanContext extracted) {
AgentTracer.TracerAPI tracer = tracer();
Supplier<Flow<Object>> startedCbAppSec =
tracer.getCallbackProvider(RequestContextSlot.APPSEC).getCallback(EVENTS.requestStarted());
Expand Down Expand Up @@ -518,10 +530,20 @@ private Flow<Void> callIGCallbackURI(

@Override
public AgentSpan beforeFinish(AgentSpan span) {
// TODO Migrate beforeFinish to Context API
onRequestEndForInstrumentationGateway(span);
// Close Serverless Gateway Inferred Span if any
// finishInferredProxySpan(context);
return super.beforeFinish(span);
}

protected void finishInferredProxySpan(Context context) {
InferredProxySpan span;
if ((span = InferredProxySpan.fromContext(context)) != null) {
span.finish();
}
}

private void onRequestEndForInstrumentationGateway(@Nonnull final AgentSpan span) {
if (span.getLocalRootSpan() != span) {
return;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,9 @@ public final class TracerConfig {
public static final String TRACE_BAGGAGE_MAX_BYTES = "trace.baggage.max.bytes";
public static final String TRACE_BAGGAGE_TAG_KEYS = "trace.baggage.tag.keys";

public static final String TRACE_INFERRED_PROXY_SERVICES_ENABLED =
"trace.inferred.proxy.services.enabled";

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

public static final String CLIENT_IP_ENABLED = "trace.client-ip.enabled";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import static datadog.trace.api.TracePropagationBehaviorExtract.IGNORE;
import static datadog.trace.bootstrap.instrumentation.api.AgentPropagation.BAGGAGE_CONCERN;
import static datadog.trace.bootstrap.instrumentation.api.AgentPropagation.DSM_CONCERN;
import static datadog.trace.bootstrap.instrumentation.api.AgentPropagation.INFERRED_PROXY_CONCERN;
import static datadog.trace.bootstrap.instrumentation.api.AgentPropagation.TRACING_CONCERN;
import static datadog.trace.bootstrap.instrumentation.api.AgentPropagation.XRAY_TRACING_CONCERN;
import static datadog.trace.common.metrics.MetricsAggregatorFactory.createMetricsAggregator;
Expand Down Expand Up @@ -91,6 +92,7 @@
import datadog.trace.core.monitor.TracerHealthMetrics;
import datadog.trace.core.propagation.ExtractedContext;
import datadog.trace.core.propagation.HttpCodec;
import datadog.trace.core.propagation.InferredProxyPropagator;
import datadog.trace.core.propagation.PropagationTags;
import datadog.trace.core.propagation.TracingPropagator;
import datadog.trace.core.propagation.XRayPropagator;
Expand Down Expand Up @@ -820,6 +822,9 @@ private CoreTracer(
&& config.getTracePropagationBehaviorExtract() != IGNORE) {
Propagators.register(BAGGAGE_CONCERN, new BaggagePropagator(config));
}
if (config.isInferredProxyPropagationEnabled()) {
Propagators.register(INFERRED_PROXY_CONCERN, new InferredProxyPropagator());
}

if (config.isCiVisibilityEnabled()) {
if (config.isCiVisibilityTraceSanitationEnabled()) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package datadog.trace.core.propagation;

import static datadog.trace.api.gateway.InferredProxySpan.fromHeaders;

import datadog.context.Context;
import datadog.context.propagation.CarrierSetter;
import datadog.context.propagation.CarrierVisitor;
import datadog.context.propagation.Propagator;
import datadog.trace.api.gateway.InferredProxySpan;
import java.util.HashMap;
import java.util.Map;
import java.util.function.BiConsumer;
import javax.annotation.ParametersAreNonnullByDefault;

/** Inferred proxy propagator. Only extract, not meant for injection. */
@ParametersAreNonnullByDefault
public class InferredProxyPropagator implements Propagator {
private static final String INFERRED_PROXY_KEY_PREFIX = "x-dd-proxy";

@Override
public <C> void inject(Context context, C carrier, CarrierSetter<C> setter) {}

@Override
public <C> Context extract(Context context, C carrier, CarrierVisitor<C> visitor) {
if (context == null || carrier == null || visitor == null) {
return context;
}
InferredProxyContextExtractor extractor = new InferredProxyContextExtractor();
visitor.forEachKeyValue(carrier, extractor);
InferredProxySpan inferredProxySpan = extractor.inferredProxySpan();
if (inferredProxySpan != null) {
context = context.with(inferredProxySpan);
}
return context;
}

/** Extract inferred proxy related headers into a map. */
private static class InferredProxyContextExtractor implements BiConsumer<String, String> {
private Map<String, String> values;

@Override
public void accept(String key, String value) {
if (key == null || key.isEmpty() || !key.startsWith(INFERRED_PROXY_KEY_PREFIX)) {
return;
}
if (values == null) {
this.values = new HashMap<>();
}
this.values.put(key, value);
}

public InferredProxySpan inferredProxySpan() {
if (this.values == null) {
return null;
}
InferredProxySpan inferredProxySpan = fromHeaders(this.values);
return inferredProxySpan.isValid() ? inferredProxySpan : null;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
package datadog.trace.core.propagation;

import static datadog.context.Context.root;
import static datadog.trace.api.gateway.InferredProxySpan.fromContext;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.params.provider.Arguments.of;

import datadog.context.Context;
import datadog.context.propagation.CarrierVisitor;
import datadog.trace.api.gateway.InferredProxySpan;
import java.util.HashMap;
import java.util.Map;
import java.util.function.BiConsumer;
import java.util.stream.Stream;
import javax.annotation.ParametersAreNonnullByDefault;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;

@DisplayName("InferredProxyPropagator Tests")
class InferredProxyPropagatorTests {
private static final String PROXY_SYSTEM_KEY = "x-dd-proxy";
private static final String PROXY_REQUEST_TIME_MS_KEY = "x-dd-proxy-request-time-ms";
private static final String PROXY_PATH_KEY = "x-dd-proxy-path";
private static final String PROXY_HTTP_METHOD_KEY = "x-dd-proxy-httpmethod";
private static final String PROXY_DOMAIN_NAME_KEY = "x-dd-proxy-domain-name";
private static final MapVisitor MAP_VISITOR = new MapVisitor();

private InferredProxyPropagator propagator;

@BeforeEach
void setUp() {
this.propagator = new InferredProxyPropagator();
}

@Test
@DisplayName("Should extract InferredProxySpan when valid headers are present")
void testSuccessfulExtraction() {
Map<String, String> headers = new HashMap<>();
headers.put(PROXY_SYSTEM_KEY, "aws-apigateway");
headers.put(PROXY_REQUEST_TIME_MS_KEY, "12345");
headers.put(PROXY_PATH_KEY, "/foo");
headers.put(PROXY_HTTP_METHOD_KEY, "GET");
headers.put(PROXY_DOMAIN_NAME_KEY, "api.example.com");

Context context = this.propagator.extract(root(), headers, MAP_VISITOR);
InferredProxySpan inferredProxySpan = fromContext(context);
assertNotNull(inferredProxySpan);
assertTrue(inferredProxySpan.isValid());
}

@ParameterizedTest(name = "{0}")
@MethodSource("invalidOrMissingHeadersProviderForPropagator")
@DisplayName("Should not create InferredProxySpan if some critical headers are missing")
void testExtractionWithMissingCriticalHeaders(String description, Map<String, String> headers) {
Context rootContext = root();
Context extractedOuterContext = this.propagator.extract(rootContext, headers, MAP_VISITOR);
InferredProxySpan inferredProxySpan = fromContext(extractedOuterContext);
assertNull(inferredProxySpan, "Invalid inferred proxy span should not be extracted");
}

static Stream<Arguments> invalidOrMissingHeadersProviderForPropagator() { // Renamed
Map<String, String> missingSystem = new HashMap<>();
missingSystem.put(PROXY_REQUEST_TIME_MS_KEY, "12345");
missingSystem.put(PROXY_PATH_KEY, "/foo");

Map<String, String> emptyValue = new HashMap<>();
emptyValue.put(PROXY_SYSTEM_KEY, "");

Map<String, String> nullValue = new HashMap<>();
nullValue.put(PROXY_SYSTEM_KEY, null);

Map<String, String> missingTime = new HashMap<>();
missingTime.put(PROXY_SYSTEM_KEY, "aws-apigw");
missingTime.put(PROXY_PATH_KEY, "/foo");

return Stream.of(
of("PROXY_SYSTEM_KEY missing", missingSystem),
of("PROXY_SYSTEM_KEY empty", emptyValue),
of("PROXY_SYSTEM_KEY null", nullValue),
of("PROXY_REQUEST_TIME_MS_KEY missing", missingTime));
}

@ParametersAreNonnullByDefault
private static class MapVisitor implements CarrierVisitor<Map<String, String>> {
@Override
public void forEachKeyValue(Map<String, String> carrier, BiConsumer<String, String> visitor) {
carrier.forEach(visitor);
}
}
}
10 changes: 10 additions & 0 deletions internal-api/src/main/java/datadog/trace/api/Config.java
Original file line number Diff line number Diff line change
Expand Up @@ -598,6 +598,7 @@
import static datadog.trace.api.config.TracerConfig.TRACE_HTTP_RESOURCE_REMOVE_TRAILING_SLASH;
import static datadog.trace.api.config.TracerConfig.TRACE_HTTP_SERVER_ERROR_STATUSES;
import static datadog.trace.api.config.TracerConfig.TRACE_HTTP_SERVER_PATH_RESOURCE_NAME_MAPPING;
import static datadog.trace.api.config.TracerConfig.TRACE_INFERRED_PROXY_SERVICES_ENABLED;
import static datadog.trace.api.config.TracerConfig.TRACE_KEEP_LATENCY_THRESHOLD_MS;
import static datadog.trace.api.config.TracerConfig.TRACE_LONG_RUNNING_ENABLED;
import static datadog.trace.api.config.TracerConfig.TRACE_LONG_RUNNING_FLUSH_INTERVAL;
Expand Down Expand Up @@ -827,6 +828,7 @@ public static String getHostName() {
private final int traceBaggageMaxItems;
private final int traceBaggageMaxBytes;
private final List<String> traceBaggageTagKeys;
private final boolean traceInferredProxyEnabled;
private final int clockSyncPeriod;
private final boolean logsInjectionEnabled;

Expand Down Expand Up @@ -1730,6 +1732,8 @@ private Config(final ConfigProvider configProvider, final InstrumenterConfig ins
tracePropagationExtractFirst =
configProvider.getBoolean(
TRACE_PROPAGATION_EXTRACT_FIRST, DEFAULT_TRACE_PROPAGATION_EXTRACT_FIRST);
traceInferredProxyEnabled =
configProvider.getBoolean(TRACE_INFERRED_PROXY_SERVICES_ENABLED, false);

clockSyncPeriod = configProvider.getInteger(CLOCK_SYNC_PERIOD, DEFAULT_CLOCK_SYNC_PERIOD);

Expand Down Expand Up @@ -3118,6 +3122,10 @@ public boolean isTracePropagationExtractFirst() {
return tracePropagationExtractFirst;
}

public boolean isInferredProxyPropagationEnabled() {
return traceInferredProxyEnabled;
}

public boolean isBaggageExtract() {
return tracePropagationStylesToExtract.contains(TracePropagationStyle.BAGGAGE);
}
Expand Down Expand Up @@ -5402,6 +5410,8 @@ public String toString() {
+ tracePropagationBehaviorExtract
+ ", tracePropagationExtractFirst="
+ tracePropagationExtractFirst
+ ", traceInferredProxyEnabled="
+ traceInferredProxyEnabled
+ ", clockSyncPeriod="
+ clockSyncPeriod
+ ", jmxFetchEnabled="
Expand Down
Loading
Loading