diff --git a/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/Agent.java b/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/Agent.java index 249453c58fb..779de19e212 100644 --- a/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/Agent.java +++ b/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/Agent.java @@ -26,6 +26,7 @@ import datadog.instrument.utils.ClassLoaderValue; import datadog.metrics.api.statsd.StatsDClientManager; import datadog.trace.api.Config; +import datadog.trace.api.InstrumenterConfig; import datadog.trace.api.Platform; import datadog.trace.api.WithGlobalTracer; import datadog.trace.api.appsec.AppSecEventTracker; @@ -337,6 +338,10 @@ public static void start( StaticEventLogger.end("crashtracking"); } + if (InstrumenterConfig.get().isLegacyContextManagerEnabled()) { + AgentTracer.installLegacyContextManager(); + } + startDatadogAgent(initTelemetry, inst); final EnumSet libraries = detectLibraries(log); diff --git a/dd-java-agent/agent-installer/src/main/java/datadog/trace/agent/tooling/nativeimage/TracerActivation.java b/dd-java-agent/agent-installer/src/main/java/datadog/trace/agent/tooling/nativeimage/TracerActivation.java index e6ff1031faa..bbe03df1c3c 100644 --- a/dd-java-agent/agent-installer/src/main/java/datadog/trace/agent/tooling/nativeimage/TracerActivation.java +++ b/dd-java-agent/agent-installer/src/main/java/datadog/trace/agent/tooling/nativeimage/TracerActivation.java @@ -8,6 +8,8 @@ import datadog.trace.agent.tooling.MeterInstaller; import datadog.trace.agent.tooling.ProfilerInstaller; import datadog.trace.agent.tooling.TracerInstaller; +import datadog.trace.api.InstrumenterConfig; +import datadog.trace.bootstrap.instrumentation.api.AgentTracer; import datadog.trace.bootstrap.instrumentation.api.ProfilingContextIntegration; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -18,6 +20,10 @@ public final class TracerActivation { public static void activate() { try { + // Bind legacy context manager (if enabled) + if (InstrumenterConfig.get().isLegacyContextManagerEnabled()) { + AgentTracer.installLegacyContextManager(); + } // Initialize meter MeterInstaller.installMeter(); // Initialize tracer diff --git a/dd-java-agent/instrumentation-testing/src/main/groovy/datadog/trace/agent/test/InstrumentationSpecification.groovy b/dd-java-agent/instrumentation-testing/src/main/groovy/datadog/trace/agent/test/InstrumentationSpecification.groovy index 8f1a6cd6df4..da04b70bbe9 100644 --- a/dd-java-agent/instrumentation-testing/src/main/groovy/datadog/trace/agent/test/InstrumentationSpecification.groovy +++ b/dd-java-agent/instrumentation-testing/src/main/groovy/datadog/trace/agent/test/InstrumentationSpecification.groovy @@ -43,6 +43,7 @@ import datadog.trace.agent.tooling.bytebuddy.matcher.ClassLoaderMatchers import datadog.trace.agent.tooling.bytebuddy.matcher.GlobalIgnores import datadog.trace.api.Config import datadog.trace.api.IdGenerationStrategy +import datadog.trace.api.InstrumenterConfig import datadog.trace.api.Pair import datadog.trace.api.ProcessTags import datadog.trace.api.TraceConfig @@ -351,18 +352,22 @@ abstract class InstrumentationSpecification extends DDSpecification implements A void setupSpec() { InstrumentationErrors.resetErrors() - AgentMeter.registerIfAbsent( - STATS_D_CLIENT, - new MonitoringImpl(STATS_D_CLIENT, 10, TimeUnit.SECONDS), - DDSketchHistograms.FACTORY - ) - // If this fails, it's likely the result of another test loading Config before it can be // injected into the bootstrap classpath. If one test extends AgentTestRunner in a module, all tests must extend assert Config.getClassLoader() == null: "Config must load on the bootstrap classpath." configurePreAgent() + if (InstrumenterConfig.get().isLegacyContextManagerEnabled()) { + AgentTracer.installLegacyContextManager() + } + + AgentMeter.registerIfAbsent( + STATS_D_CLIENT, + new MonitoringImpl(STATS_D_CLIENT, 10, TimeUnit.SECONDS), + DDSketchHistograms.FACTORY + ) + TEST_DATA_STREAMS_WRITER = new RecordingDatastreamsPayloadWriter() DDAgentFeaturesDiscovery features = new MockFeaturesDiscovery(true) diff --git a/dd-java-agent/instrumentation-testing/src/main/java/datadog/trace/agent/test/AbstractInstrumentationTest.java b/dd-java-agent/instrumentation-testing/src/main/java/datadog/trace/agent/test/AbstractInstrumentationTest.java index 5f724853972..dcf7fee6040 100644 --- a/dd-java-agent/instrumentation-testing/src/main/java/datadog/trace/agent/test/AbstractInstrumentationTest.java +++ b/dd-java-agent/instrumentation-testing/src/main/java/datadog/trace/agent/test/AbstractInstrumentationTest.java @@ -13,8 +13,10 @@ import datadog.trace.agent.tooling.bytebuddy.matcher.ClassLoaderMatchers; import datadog.trace.api.Config; import datadog.trace.api.IdGenerationStrategy; +import datadog.trace.api.InstrumenterConfig; import datadog.trace.bootstrap.InstrumentationErrors; import datadog.trace.bootstrap.instrumentation.api.AgentSpan; +import datadog.trace.bootstrap.instrumentation.api.AgentTracer; import datadog.trace.bootstrap.instrumentation.api.AgentTracer.TracerAPI; import datadog.trace.common.writer.ListWriter; import datadog.trace.core.CoreTracer; @@ -75,6 +77,10 @@ static void initAll() { // injected into the bootstrap classpath. assertNull(Config.class.getClassLoader(), "Config must load on the bootstrap classpath."); + if (InstrumenterConfig.get().isLegacyContextManagerEnabled()) { + AgentTracer.installLegacyContextManager(); + } + // Create shared test writer and tracer writer = new ListWriter(); CoreTracer coreTracer = diff --git a/dd-java-agent/instrumentation/aws-java/aws-java-sqs-2.0/src/test/java/SqsClientTest.java b/dd-java-agent/instrumentation/aws-java/aws-java-sqs-2.0/src/test/java/SqsClientTest.java index 33c17c887ff..c8cd8ff9058 100644 --- a/dd-java-agent/instrumentation/aws-java/aws-java-sqs-2.0/src/test/java/SqsClientTest.java +++ b/dd-java-agent/instrumentation/aws-java/aws-java-sqs-2.0/src/test/java/SqsClientTest.java @@ -38,6 +38,7 @@ import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import software.amazon.awssdk.auth.credentials.AnonymousCredentialsProvider; import software.amazon.awssdk.core.SdkBytes; @@ -529,6 +530,7 @@ boolean isDataStreamsEnabled() { } } +@Disabled("TODO(APMLP-829): non-legacy ContextManager migration still in-progress") @WithConfig(key = GeneralConfig.DATA_STREAMS_ENABLED, value = "false") @WithConfig(key = TraceInstrumentationConfig.LEGACY_CONTEXT_MANAGER_ENABLED, value = "false") class SqsClientV0ContextSwapForkedTest extends SqsClientV0TestBase {} @@ -756,6 +758,7 @@ class SqsClientV0ReceiveIterationTest extends SqsClientV0ReceiveIterationTestBas @WithConfig(key = GeneralConfig.DATA_STREAMS_ENABLED, value = "true") class SqsClientV0DataStreamsReceiveIterationTest extends SqsClientV0ReceiveIterationTestBase {} +@Disabled("TODO(APMLP-829): non-legacy ContextManager migration still in-progress") @WithConfig(key = GeneralConfig.DATA_STREAMS_ENABLED, value = "false") @WithConfig(key = TraceInstrumentationConfig.LEGACY_CONTEXT_MANAGER_ENABLED, value = "false") class SqsClientV0ContextSwapReceiveIterationForkedTest extends SqsClientV0ReceiveIterationTestBase { diff --git a/dd-java-agent/instrumentation/jms/javax-jms-1.1/src/test/groovy/JMS1Test.groovy b/dd-java-agent/instrumentation/jms/javax-jms-1.1/src/test/groovy/JMS1Test.groovy index f24b7e8f9ff..1b8c3277d6d 100644 --- a/dd-java-agent/instrumentation/jms/javax-jms-1.1/src/test/groovy/JMS1Test.groovy +++ b/dd-java-agent/instrumentation/jms/javax-jms-1.1/src/test/groovy/JMS1Test.groovy @@ -32,6 +32,7 @@ import javax.jms.TopicSession import jms10mock.Jms10ConnectionFactory import org.apache.activemq.command.ActiveMQTextMessage import org.apache.activemq.junit.EmbeddedActiveMQBroker +import spock.lang.Ignore import spock.lang.Shared abstract class JMS1Test extends VersionedNamingTestBase { @@ -1061,6 +1062,7 @@ class JMS1V0Test extends JMS1Test { } } +@Ignore("TODO(APMLP-829): non-legacy ContextManager migration still in-progress") class JMSContextSwapForkedTest extends JMS1V0Test { @Override protected void configurePreAgent() { diff --git a/dd-java-agent/instrumentation/kafka/kafka-clients-0.11/src/test/groovy/KafkaClientTestBase.groovy b/dd-java-agent/instrumentation/kafka/kafka-clients-0.11/src/test/groovy/KafkaClientTestBase.groovy index 8fd96e55745..ece63ae4703 100644 --- a/dd-java-agent/instrumentation/kafka/kafka-clients-0.11/src/test/groovy/KafkaClientTestBase.groovy +++ b/dd-java-agent/instrumentation/kafka/kafka-clients-0.11/src/test/groovy/KafkaClientTestBase.groovy @@ -42,6 +42,7 @@ import org.springframework.kafka.listener.MessageListener import org.springframework.kafka.test.rule.KafkaEmbedded import org.springframework.kafka.test.utils.ContainerTestUtils import org.springframework.kafka.test.utils.KafkaTestUtils +import spock.lang.Ignore import spock.lang.Shared import java.util.concurrent.ExecutionException @@ -1545,6 +1546,7 @@ class KafkaClientDataStreamsDisabledForkedTest extends KafkaClientTestBase { } } +@Ignore("TODO(APMLP-829): non-legacy ContextManager migration still in-progress") class KafkaClientContextSwapForkedTest extends KafkaClientV0ForkedTest { void configurePreAgent() { super.configurePreAgent() diff --git a/dd-java-agent/instrumentation/kafka/kafka-clients-3.8/src/test/groovy/KafkaClientTestBase.groovy b/dd-java-agent/instrumentation/kafka/kafka-clients-3.8/src/test/groovy/KafkaClientTestBase.groovy index 5d505c5398c..90212ebf02b 100644 --- a/dd-java-agent/instrumentation/kafka/kafka-clients-3.8/src/test/groovy/KafkaClientTestBase.groovy +++ b/dd-java-agent/instrumentation/kafka/kafka-clients-3.8/src/test/groovy/KafkaClientTestBase.groovy @@ -48,6 +48,8 @@ import static datadog.trace.agent.test.utils.TraceUtils.runUnderTrace import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.activeSpan import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.isAsyncPropagationEnabled +import spock.lang.Ignore + abstract class KafkaClientTestBase extends VersionedNamingTestBase { static final SHARED_TOPIC = "shared.topic" @@ -1211,6 +1213,7 @@ class KafkaClientDataStreamsDisabledForkedTest extends KafkaClientTestBase { } } +@Ignore("TODO(APMLP-829): non-legacy ContextManager migration still in-progress") class KafkaClientContextSwapForkedTest extends KafkaClientV0ForkedTest { void configurePreAgent() { super.configurePreAgent() diff --git a/dd-trace-core/src/jmh/java/datadog/context/ContextManagerBenchmark.java b/dd-trace-core/src/jmh/java/datadog/context/ContextManagerBenchmark.java index 039f0bad160..d15d5bce2ff 100644 --- a/dd-trace-core/src/jmh/java/datadog/context/ContextManagerBenchmark.java +++ b/dd-trace-core/src/jmh/java/datadog/context/ContextManagerBenchmark.java @@ -107,9 +107,34 @@ public void setup() { } static ContextManager createManager(String type) { - return "Continuable".equals(type) - ? new ContinuableScopeManager(0, false) - : ThreadLocalContextManager.INSTANCE; + if ("Continuable".equals(type)) { + ContinuableScopeManager csm = new ContinuableScopeManager(0, false); + return new ContextManager() { + @Override + public Context current() { + return csm.current(); + } + + @Override + public ContextScope attach(Context ctx) { + return csm.attach(ctx); + } + + @Override + public Context swap(Context ctx) { + return csm.swap(ctx); + } + + @Override + public ContextContinuation capture(Context ctx) { + return csm.capture(ctx); + } + + @Override + public void addListener(ContextListener l) {} + }; + } + return ThreadLocalContextManager.INSTANCE; } static Context[] createContexts() { diff --git a/dd-trace-core/src/main/java/datadog/trace/core/CoreTracer.java b/dd-trace-core/src/main/java/datadog/trace/core/CoreTracer.java index 7ad4497beca..620c9ee01a2 100644 --- a/dd-trace-core/src/main/java/datadog/trace/core/CoreTracer.java +++ b/dd-trace-core/src/main/java/datadog/trace/core/CoreTracer.java @@ -22,6 +22,9 @@ import datadog.communication.ddagent.DDAgentFeaturesDiscovery; import datadog.communication.ddagent.ExternalAgentLauncher; import datadog.communication.ddagent.SharedCommunicationObjects; +import datadog.context.Context; +import datadog.context.ContextContinuation; +import datadog.context.ContextScope; import datadog.context.propagation.Propagators; import datadog.environment.ThreadSupport; import datadog.logging.RatelimitedLogger; @@ -2465,4 +2468,24 @@ static TagMap withTracerTags( } return result.freeze(); } + + @Override + public Context currentContext() { + return scopeManager.current(); + } + + @Override + public ContextScope attach(Context context) { + return scopeManager.attach(context); + } + + @Override + public Context swap(Context context) { + return scopeManager.swap(context); + } + + @Override + public ContextContinuation capture(Context context) { + return scopeManager.capture(context); + } } diff --git a/dd-trace-core/src/main/java/datadog/trace/core/scopemanager/ContinuableScopeManager.java b/dd-trace-core/src/main/java/datadog/trace/core/scopemanager/ContinuableScopeManager.java index 04fbd637bb3..e77329493d3 100644 --- a/dd-trace-core/src/main/java/datadog/trace/core/scopemanager/ContinuableScopeManager.java +++ b/dd-trace-core/src/main/java/datadog/trace/core/scopemanager/ContinuableScopeManager.java @@ -14,8 +14,6 @@ import datadog.context.Context; import datadog.context.ContextContinuation; -import datadog.context.ContextListener; -import datadog.context.ContextManager; import datadog.context.ContextScope; import datadog.logging.RatelimitedLogger; import datadog.trace.api.Config; @@ -47,7 +45,7 @@ * from being reported even if all related spans are finished. It also delegates to other * ScopeInterceptors to provide additional functionality. */ -public final class ContinuableScopeManager implements ContextManager { +public final class ContinuableScopeManager { static final Logger log = LoggerFactory.getLogger(ContinuableScopeManager.class); static final RatelimitedLogger ratelimitedLog = new RatelimitedLogger(log, 1, MINUTES); @@ -95,8 +93,6 @@ public ContinuableScopeManager( this.profilingContextIntegration = profilingContextIntegration; this.profilingEnabled = !(profilingContextIntegration instanceof ProfilingContextIntegration.NoOp); - - ContextManager.register(this); } public AgentScope activateSpan(final AgentSpan span) { @@ -370,18 +366,15 @@ ScopeStack scopeStack() { return this.tlsScopeStack.get(); } - @Override public Context current() { final ContinuableScope active = scopeStack().active(); return active == null ? Context.root() : active.context; } - @Override public ContextScope attach(@NonNull Context context) { return activate(context); } - @Override public Context swap(@NonNull Context context) { ScopeStack oldStack = tlsScopeStack.get(); ContinuableScope oldScope = oldStack.top; @@ -412,7 +405,6 @@ public Context swap(@NonNull Context context) { return new ScopeContext(oldStack); } - @Override public ContextContinuation capture(@NonNull Context context) { // respect async propagation flag for Context.current().capture() ContinuableScope activeScope = scopeStack().active(); @@ -431,12 +423,6 @@ public ContextContinuation capture(@NonNull Context context) { return new ScopeContinuation(this, context, CONTEXT, traceCollector).register(); } - @Override - public void addListener(@NonNull ContextListener unused) { - // this new API is not expected to be used in legacy mode... - log.warn("Unexpected call to ContextManager.addListener(...)"); - } - static final class ScopeStackThreadLocal extends ThreadLocal { private final ProfilingContextIntegration profilingContextIntegration; diff --git a/dd-trace-core/src/test/java/datadog/trace/core/scopemanager/ScopeManagerTest.java b/dd-trace-core/src/test/java/datadog/trace/core/scopemanager/ScopeManagerForkedTest.java similarity index 98% rename from dd-trace-core/src/test/java/datadog/trace/core/scopemanager/ScopeManagerTest.java rename to dd-trace-core/src/test/java/datadog/trace/core/scopemanager/ScopeManagerForkedTest.java index cf1748cece0..16203256a44 100644 --- a/dd-trace-core/src/test/java/datadog/trace/core/scopemanager/ScopeManagerTest.java +++ b/dd-trace-core/src/test/java/datadog/trace/core/scopemanager/ScopeManagerForkedTest.java @@ -2,8 +2,8 @@ import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.noopContinuation; import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.noopSpan; -import static datadog.trace.core.scopemanager.ScopeManagerTest.EVENT.ACTIVATE; -import static datadog.trace.core.scopemanager.ScopeManagerTest.EVENT.CLOSE; +import static datadog.trace.core.scopemanager.ScopeManagerForkedTest.EVENT.ACTIVATE; +import static datadog.trace.core.scopemanager.ScopeManagerForkedTest.EVENT.CLOSE; import static datadog.trace.test.util.GCUtils.awaitGC; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -36,6 +36,7 @@ import datadog.trace.api.scopemanager.ScopeListener; import datadog.trace.bootstrap.instrumentation.api.AgentScope; import datadog.trace.bootstrap.instrumentation.api.AgentSpan; +import datadog.trace.bootstrap.instrumentation.api.AgentTracer; import datadog.trace.bootstrap.instrumentation.api.ProfilingContextIntegration; import datadog.trace.common.writer.ListWriter; import datadog.trace.context.TraceScope; @@ -56,13 +57,14 @@ import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; import org.tabletest.junit.TableTest; -class ScopeManagerTest extends DDCoreJavaSpecification { +class ScopeManagerForkedTest extends DDCoreJavaSpecification { enum EVENT { ACTIVATE, @@ -83,6 +85,11 @@ protected boolean useStrictTraceWrites() { ProfilingContextIntegration profilingContext; Stateful state; + @BeforeAll + static void installLegacyContextManager() { + AgentTracer.installLegacyContextManager(); + } + @BeforeEach void setup() { state = mock(Stateful.class); @@ -91,6 +98,7 @@ void setup() { when(profilingContext.name()).thenReturn("mock"); writer = new ListWriter(); tracer = tracerBuilder().writer(writer).profilingContextIntegration(profilingContext).build(); + AgentTracer.forceRegister(tracer); scopeManager = ScopeManagerTestBridge.getScopeManager(tracer); eventCountingListener = new EventCountingListener(); scopeManager.addScopeListener(eventCountingListener); diff --git a/internal-api/build.gradle.kts b/internal-api/build.gradle.kts index 6e99abe89d2..65be1563eda 100644 --- a/internal-api/build.gradle.kts +++ b/internal-api/build.gradle.kts @@ -91,6 +91,7 @@ extra["excludedClassesCoverage"] = listOf( "datadog.trace.bootstrap.instrumentation.api.AgentSpan", "datadog.trace.bootstrap.instrumentation.api.AgentSpanContext", "datadog.trace.bootstrap.instrumentation.api.AgentTracer", + "datadog.trace.bootstrap.instrumentation.api.AgentTracer.LegacyContextManager", "datadog.trace.bootstrap.instrumentation.api.AgentTracer.NoopAgentHistogram", "datadog.trace.bootstrap.instrumentation.api.AgentTracer.NoopAgentTraceCollector", "datadog.trace.bootstrap.instrumentation.api.AgentTracer.NoopTraceConfig", diff --git a/internal-api/src/main/java/datadog/trace/bootstrap/instrumentation/api/AgentTracer.java b/internal-api/src/main/java/datadog/trace/bootstrap/instrumentation/api/AgentTracer.java index 8d882b882bc..c13c35f4245 100644 --- a/internal-api/src/main/java/datadog/trace/bootstrap/instrumentation/api/AgentTracer.java +++ b/internal-api/src/main/java/datadog/trace/bootstrap/instrumentation/api/AgentTracer.java @@ -1,5 +1,10 @@ package datadog.trace.bootstrap.instrumentation.api; +import datadog.context.Context; +import datadog.context.ContextContinuation; +import datadog.context.ContextListener; +import datadog.context.ContextManager; +import datadog.context.ContextScope; import datadog.trace.api.ConfigDefaults; import datadog.trace.api.DDTraceId; import datadog.trace.api.EndpointCheckpointer; @@ -402,6 +407,20 @@ default AgentSpan blackholeSpan() { void updatePreferredServiceName(String serviceName, CharSequence source); void addShutdownListener(Runnable listener); + + // these methods are only used for legacy context manager migration + + @Deprecated + Context currentContext(); + + @Deprecated + ContextScope attach(Context context); + + @Deprecated + Context swap(Context context); + + @Deprecated + ContextContinuation capture(Context context); } public interface SpanBuilder { @@ -653,6 +672,26 @@ public TraceConfig captureTraceConfig() { public void updatePreferredServiceName(String serviceName, CharSequence preferredServiceName) { // no ops } + + @Override + public Context currentContext() { + return Context.root(); + } + + @Override + public ContextScope attach(Context context) { + return noopScope(); + } + + @Override + public Context swap(Context context) { + return Context.root(); + } + + @Override + public ContextContinuation capture(Context context) { + return noopContinuation(); + } } public static class NoopAgentTraceCollector implements AgentTraceCollector { @@ -739,4 +778,39 @@ public List getDataStreamsTransactionExtractors return null; } } + + /** Must be called ahead of instrumentation, before any use of the Context API. */ + public static void installLegacyContextManager() { + ContextManager.register(LegacyContextManager.INSTANCE); + } + + /** Shim mapping new Context API to legacy scope manager. */ + static final class LegacyContextManager implements ContextManager { + static final LegacyContextManager INSTANCE = new LegacyContextManager(); + + @Override + public Context current() { + return AgentTracer.get().currentContext(); + } + + @Override + public ContextScope attach(@Nonnull Context context) { + return AgentTracer.get().attach(context); + } + + @Override + public Context swap(@Nonnull Context context) { + return AgentTracer.get().swap(context); + } + + @Override + public ContextContinuation capture(@Nonnull Context context) { + return AgentTracer.get().capture(context); + } + + @Override + public void addListener(@Nonnull ContextListener listener) { + // this method is never used in legacy mode... + } + } }