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
Original file line number Diff line number Diff line change
Expand Up @@ -310,7 +310,7 @@ public static Set<InstrumenterModule.TargetSystem> getEnabledSystems() {
EnumSet<InstrumenterModule.TargetSystem> enabledSystems =
EnumSet.of(InstrumenterModule.TargetSystem.CONTEXT_TRACKING);
InstrumenterConfig cfg = InstrumenterConfig.get();
if (cfg.isTraceEnabled()) {
if (cfg.isTraceEnabled() || cfg.isPropagateContextEnabled()) {
enabledSystems.add(InstrumenterModule.TargetSystem.TRACING);
}
if (cfg.isProfilingEnabled()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ public class TracerInstaller {
public static synchronized void installGlobalTracer(
SharedCommunicationObjects sharedCommunicationObjects,
ProfilingContextIntegration profilingContextIntegration) {
if (Config.get().isTraceEnabled() || Config.get().isCiVisibilityEnabled()) {
if (Config.get().isTraceEnabled()
|| Config.get().isCiVisibilityEnabled()
|| Config.get().isPropagateContextEnabled()) {
Comment thread
aviramha marked this conversation as resolved.
if (!(GlobalTracer.get() instanceof CoreTracer)) {
CoreTracer tracer =
CoreTracer.builder()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ public final class ConfigDefaults {
static final boolean DEFAULT_CODE_ORIGIN_FOR_SPANS_INTERFACE_SUPPORT = false;
static final int DEFAULT_CODE_ORIGIN_MAX_USER_FRAMES = 8;
static final boolean DEFAULT_TRACE_ENABLED = true;
static final boolean DEFAULT_PROPAGATE_CONTEXT = false;
public static final boolean DEFAULT_TRACE_OTEL_ENABLED = false;
static final boolean DEFAULT_INTEGRATIONS_ENABLED = true;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ public final class TraceInstrumentationConfig {
"code.origin.for.spans.interface.support";
public static final String CODE_ORIGIN_MAX_USER_FRAMES = "code.origin.max.user.frames";
public static final String TRACE_ENABLED = "trace.enabled";
public static final String PROPAGATE_CONTEXT = "propagate.context";
public static final String INTEGRATIONS_ENABLED = "integrations.enabled";
public static final String DETAILED_INSTRUMENTATION_ERRORS = "detailed.instrumentation.errors";

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package datadog.trace.common.writer;

import datadog.trace.core.DDSpan;
import java.util.List;

/**
* A writer that discards all traces. Used when the tracer is only installed to propagate context
* (see {@code TraceInstrumentationConfig.PROPAGATE_CONTEXT}) and no trace data must be reported.
*/
public class NoOpWriter implements Writer {

@Override
public void write(final List<DDSpan> trace) {}

@Override
public void start() {}

@Override
public boolean flush() {
return true;
}

@Override
public void close() {}

@Override
public void incrementDropCounts(int spanCount) {}

@Override
public String toString() {
return "NoOpWriter { }";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import datadog.communication.ddagent.SharedCommunicationObjects;
import datadog.trace.api.Config;
import datadog.trace.api.civisibility.config.BazelMode;
import datadog.trace.api.config.TraceInstrumentationConfig;
import datadog.trace.api.intake.TrackType;
import datadog.trace.common.sampling.Sampler;
import datadog.trace.common.sampling.SingleSpanSampler;
Expand Down Expand Up @@ -44,6 +45,12 @@ public static Writer createWriter(
final Sampler sampler,
final SingleSpanSampler singleSpanSampler,
final HealthMetrics healthMetrics) {
if (config.isContextPropagationOnly()) {
log.info(
"Tracing is disabled but context propagation is enabled ({}): traces will not be reported",
TraceInstrumentationConfig.PROPAGATE_CONTEXT);
return new NoOpWriter();
}
return createWriter(
config, commObjects, sampler, singleSpanSampler, healthMetrics, config.getWriterType());
}
Expand Down
10 changes: 8 additions & 2 deletions dd-trace-core/src/main/java/datadog/trace/core/CoreTracer.java
Original file line number Diff line number Diff line change
Expand Up @@ -704,7 +704,9 @@ private CoreTracer(

this.dynamicConfig =
DynamicConfig.create(ConfigSnapshot::new)
.setTracingEnabled(true) // implied by installation of CoreTracer
// tracing is implied by the installation of CoreTracer, unless the tracer is only
// installed to propagate context without reporting traces
.setTracingEnabled(!config.isContextPropagationOnly())
.setRuntimeMetricsEnabled(config.isRuntimeMetricsEnabled())
.setLogsInjectionEnabled(config.isLogsInjectionEnabled())
.setDataStreamsEnabled(config.isDataStreamsEnabled())
Expand Down Expand Up @@ -838,8 +840,12 @@ private CoreTracer(
this.healthMetrics);
HttpCodec.Extractor tracingExtractor = orgGuard.decorateExtractor(baseExtractor);
HttpCodec.Injector tracingInjector = orgGuard.decorateInjector(injector);
// Context propagation is also enabled when explicitly requested via propagate.context, so that
// context keeps flowing even when APM tracing is disabled.
boolean propagationEnabled =
config.isApmTracingEnabled() || config.isPropagateContextEnabled();
TracingPropagator tracingPropagator =
new TracingPropagator(config.isApmTracingEnabled(), tracingInjector, tracingExtractor);
new TracingPropagator(propagationEnabled, tracingInjector, tracingExtractor);
Propagators.register(TRACING_CONCERN, tracingPropagator);
Propagators.register(XRAY_TRACING_CONCERN, new XRayPropagator(config), false);
if (config.isDataStreamsEnabled()) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package datadog.trace.core;

import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;

import datadog.context.Context;
import datadog.context.propagation.Propagators;
import datadog.trace.api.config.TraceInstrumentationConfig;
import datadog.trace.bootstrap.instrumentation.api.AgentSpan;
import datadog.trace.common.writer.DDAgentWriter;
import datadog.trace.common.writer.NoOpWriter;
import datadog.trace.junit.utils.config.WithConfig;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.Timeout;

/**
* Tests for the propagate-only mode enabled by DD_PROPAGATE_CONTEXT: context propagation stays
* active while no trace data is reported to Datadog.
*/
@Timeout(value = 10, unit = TimeUnit.SECONDS)
class ContextPropagationOnlyTest extends DDCoreJavaSpecification {

@Test
@WithConfig(key = TraceInstrumentationConfig.TRACE_ENABLED, value = "false")
@WithConfig(key = TraceInstrumentationConfig.PROPAGATE_CONTEXT, value = "true")
void verifyPropagateOnlyModeDoesNotReportTraces() {
CoreTracer tracer = tracerBuilder().build();

assertInstanceOf(NoOpWriter.class, tracer.writer);
assertFalse(tracer.captureTraceConfig().isTraceEnabled());
}

@Test
@WithConfig(key = TraceInstrumentationConfig.TRACE_ENABLED, value = "false")
@WithConfig(key = TraceInstrumentationConfig.PROPAGATE_CONTEXT, value = "true")
void verifyPropagateOnlyModeStillInjectsContext() {
CoreTracer tracer = tracerBuilder().build();
AgentSpan span = tracer.buildSpan("test", "operation").start();
try {
Map<String, String> carrier = new HashMap<>();
Propagators.defaultPropagator().inject(Context.root().with(span), carrier, Map::put);

assertNotNull(carrier.get("traceparent"), "traceparent missing: " + carrier);
assertNotNull(carrier.get("x-datadog-trace-id"), "x-datadog-trace-id missing: " + carrier);
assertTrue(
carrier.get("traceparent").contains(span.context().getTraceId().toHexString()),
"traceparent does not match trace id: " + carrier);
} finally {
span.finish();
}
}

@Test
@WithConfig(key = TraceInstrumentationConfig.PROPAGATE_CONTEXT, value = "true")
void verifyFlagHasNoEffectWhenTracingEnabled() {
CoreTracer tracer = tracerBuilder().build();

assertInstanceOf(DDAgentWriter.class, tracer.writer);
assertTrue(tracer.captureTraceConfig().isTraceEnabled());
}

@Test
@WithConfig(key = TraceInstrumentationConfig.TRACE_ENABLED, value = "false")
void verifyTracingDisabledWithoutFlagKeepsCurrentBehavior() {
CoreTracer tracer = tracerBuilder().build();

assertInstanceOf(DDAgentWriter.class, tracer.writer);
assertTrue(tracer.captureTraceConfig().isTraceEnabled());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -199,4 +199,21 @@ void testApmTracingDisabledPropagatorStopPropagation(boolean apmTracingEnabled)
span.finish();
tracer.close();
}

@Test
void testPropagateContextOverridesApmTracingDisabled() {
// APM tracing is off, but propagate.context forces context to keep flowing downstream
injectSysConfig("apm.tracing.enabled", "false");
injectSysConfig("propagate.context", "true");
CoreTracer tracer = tracerBuilder().build();
AgentSpan span = tracer.buildSpan("test", "operation").start();

Map<String, String> carrier = new HashMap<>();
Propagators.defaultPropagator().inject(span, carrier, Map::put);

assertNotNull(carrier.get("traceparent"), "traceparent missing: " + carrier);

span.finish();
tracer.close();
}
}
13 changes: 13 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 @@ -3410,6 +3410,19 @@ public boolean isTraceEnabled() {
return instrumenterConfig.isTraceEnabled();
}

public boolean isPropagateContextEnabled() {
return instrumenterConfig.isPropagateContextEnabled();
}

/**
* Whether the tracer runs only to propagate context (DD_PROPAGATE_CONTEXT=true while tracing is
* disabled): spans are created and context is injected into downstream requests, but traces are
* never reported to Datadog.
*/
public boolean isContextPropagationOnly() {
return isPropagateContextEnabled() && !isTraceEnabled() && !isCiVisibilityEnabled();
}

public boolean isServiceDiscoveryEnabled() {
return serviceDiscoveryEnabled;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import static datadog.trace.api.ConfigDefaults.DEFAULT_MEASURE_METHODS;
import static datadog.trace.api.ConfigDefaults.DEFAULT_MEASURE_NATIVE_METHODS;
import static datadog.trace.api.ConfigDefaults.DEFAULT_METRICS_OTEL_ENABLED;
import static datadog.trace.api.ConfigDefaults.DEFAULT_PROPAGATE_CONTEXT;
import static datadog.trace.api.ConfigDefaults.DEFAULT_RESOLVER_RESET_INTERVAL;
import static datadog.trace.api.ConfigDefaults.DEFAULT_RUM_ENABLED;
import static datadog.trace.api.ConfigDefaults.DEFAULT_RUNTIME_CONTEXT_FIELD_INJECTION;
Expand Down Expand Up @@ -68,6 +69,7 @@
import static datadog.trace.api.config.TraceInstrumentationConfig.LEGACY_CONTEXT_MANAGER_ENABLED;
import static datadog.trace.api.config.TraceInstrumentationConfig.MEASURE_METHODS;
import static datadog.trace.api.config.TraceInstrumentationConfig.MEASURE_NATIVE_METHODS;
import static datadog.trace.api.config.TraceInstrumentationConfig.PROPAGATE_CONTEXT;
import static datadog.trace.api.config.TraceInstrumentationConfig.RESOLVER_CACHE_CONFIG;
import static datadog.trace.api.config.TraceInstrumentationConfig.RESOLVER_CACHE_DIR;
import static datadog.trace.api.config.TraceInstrumentationConfig.RESOLVER_NAMES_ARE_UNIQUE;
Expand Down Expand Up @@ -151,6 +153,7 @@ public class InstrumenterConfig {
private final boolean codeOriginEnabled;
private final boolean codeOriginInterfaceSupport;
private final boolean traceEnabled;
private final boolean propagateContextEnabled;
private final boolean traceOtelEnabled;
private final boolean metricsOtelEnabled;
private final boolean logsOtelEnabled;
Expand Down Expand Up @@ -262,6 +265,8 @@ private InstrumenterConfig() {
CODE_ORIGIN_FOR_SPANS_INTERFACE_SUPPORT,
DEFAULT_CODE_ORIGIN_FOR_SPANS_INTERFACE_SUPPORT);
traceEnabled = configProvider.getBoolean(TRACE_ENABLED, DEFAULT_TRACE_ENABLED);
propagateContextEnabled =
configProvider.getBoolean(PROPAGATE_CONTEXT, DEFAULT_PROPAGATE_CONTEXT);
traceOtelEnabled = configProvider.getBoolean(TRACE_OTEL_ENABLED, DEFAULT_TRACE_OTEL_ENABLED);
metricsOtelEnabled =
configProvider.getBoolean(METRICS_OTEL_ENABLED, DEFAULT_METRICS_OTEL_ENABLED);
Expand Down Expand Up @@ -457,6 +462,14 @@ public boolean isTraceEnabled() {
return traceEnabled;
}

/**
* Whether context propagation (tracecontext, baggage, etc.) must stay active even when tracing is
* disabled. See {@code TraceInstrumentationConfig.PROPAGATE_CONTEXT}.
*/
public boolean isPropagateContextEnabled() {
return propagateContextEnabled;
}

public boolean isTraceOtelEnabled() {
return traceOtelEnabled;
}
Expand Down Expand Up @@ -767,6 +780,8 @@ public String toString() {
+ integrationsEnabled
+ ", traceEnabled="
+ traceEnabled
+ ", propagateContextEnabled="
+ propagateContextEnabled
+ ", traceOtelEnabled="
+ traceOtelEnabled
+ ", metricsOtelEnabled="
Expand Down
8 changes: 8 additions & 0 deletions metadata/supported-configurations.json
Original file line number Diff line number Diff line change
Expand Up @@ -3425,6 +3425,14 @@
"aliases": []
}
],
"DD_PROPAGATE_CONTEXT": [
{
"version": "A",
"type": "boolean",
"default": "false",
"aliases": []
}
],
"DD_PROPAGATION_EXTRACT_LOG_HEADER_NAMES_ENABLED": [
{
"version": "A",
Expand Down