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 7377830a905..80fdb5accc4 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 @@ -1197,10 +1197,6 @@ && isExplicitlyDisabled(TraceInstrumentationConfig.CODE_ORIGIN_FOR_SPANS_ENABLED && isExplicitlyDisabled(DebuggerConfig.DISTRIBUTED_DEBUGGER_ENABLED)) { return; } - if (!remoteConfigEnabled) { - log.warn("Cannot enable Dynamic Instrumentation because Remote Configuration is not enabled"); - return; - } startDebuggerAgent(inst, scoClass, sco); } diff --git a/dd-java-agent/agent-debugger/debugger-el/src/test/java/com/datadog/debugger/el/expressions/ComparisonExpressionTest.java b/dd-java-agent/agent-debugger/debugger-el/src/test/java/com/datadog/debugger/el/expressions/ComparisonExpressionTest.java index a72e20acfb8..8630179a7dc 100644 --- a/dd-java-agent/agent-debugger/debugger-el/src/test/java/com/datadog/debugger/el/expressions/ComparisonExpressionTest.java +++ b/dd-java-agent/agent-debugger/debugger-el/src/test/java/com/datadog/debugger/el/expressions/ComparisonExpressionTest.java @@ -209,6 +209,7 @@ void evaluateOperatorStrings( ComparisonExpression expression = new ComparisonExpression(left, right, operator); assertEquals(expected, expression.evaluate(NoopResolver.INSTANCE)); assertEquals(prettyPrint, print(expression)); + fail("should not pass"); } private static Stream expressionStrs() { diff --git a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/agent/DebuggerAgent.java b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/agent/DebuggerAgent.java index f18771de9c7..30565b9b3b8 100644 --- a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/agent/DebuggerAgent.java +++ b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/agent/DebuggerAgent.java @@ -40,7 +40,6 @@ import java.lang.ref.WeakReference; import java.nio.file.Path; import java.nio.file.Paths; -import java.time.Duration; import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -210,13 +209,7 @@ public static void startExceptionReplay() { Config config = Config.get(); commonInit(config); initClassNameFilter(); - exceptionDebugger = - new DefaultExceptionDebugger( - configurationUpdater, - classNameFilter, - Duration.ofSeconds(config.getDebuggerExceptionCaptureInterval()), - config.getDebuggerMaxExceptionPerSecond(), - config.getDebuggerExceptionMaxCapturedFrames()); + exceptionDebugger = new DefaultExceptionDebugger(configurationUpdater, classNameFilter, config); DebuggerContext.initExceptionDebugger(exceptionDebugger); LOGGER.info("Started Exception Replay"); } diff --git a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/exception/DefaultExceptionDebugger.java b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/exception/DefaultExceptionDebugger.java index 981f52208b8..4977dfcb277 100644 --- a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/exception/DefaultExceptionDebugger.java +++ b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/exception/DefaultExceptionDebugger.java @@ -9,6 +9,7 @@ import com.datadog.debugger.sink.Snapshot; import com.datadog.debugger.util.CircuitBreaker; import com.datadog.debugger.util.ExceptionHelper; +import datadog.trace.api.Config; import datadog.trace.bootstrap.debugger.DebuggerContext; import datadog.trace.bootstrap.debugger.DebuggerContext.ClassNameFilter; import datadog.trace.bootstrap.instrumentation.api.AgentSpan; @@ -39,19 +40,20 @@ public class DefaultExceptionDebugger implements DebuggerContext.ExceptionDebugg private final ClassNameFilter classNameFiltering; private final CircuitBreaker circuitBreaker; private final int maxCapturedFrames; + private final boolean applyConfigAsync; public DefaultExceptionDebugger( ConfigurationUpdater configurationUpdater, ClassNameFilter classNameFiltering, - Duration captureInterval, - int maxExceptionPerSecond, - int maxCapturedFrames) { + Config config) { this( - new ExceptionProbeManager(classNameFiltering, captureInterval), + new ExceptionProbeManager( + classNameFiltering, Duration.ofSeconds(config.getDebuggerExceptionCaptureInterval())), configurationUpdater, classNameFiltering, - maxExceptionPerSecond, - maxCapturedFrames); + config.getDebuggerMaxExceptionPerSecond(), + config.getDebuggerExceptionMaxCapturedFrames(), + config.isDebuggerExceptionAsyncConfig()); } DefaultExceptionDebugger( @@ -59,12 +61,14 @@ public DefaultExceptionDebugger( ConfigurationUpdater configurationUpdater, ClassNameFilter classNameFiltering, int maxExceptionPerSecond, - int maxCapturedFrames) { + int maxCapturedFrames, + boolean applyConfigAsync) { this.exceptionProbeManager = exceptionProbeManager; this.configurationUpdater = configurationUpdater; this.classNameFiltering = classNameFiltering; this.circuitBreaker = new CircuitBreaker(maxExceptionPerSecond, Duration.ofSeconds(1)); this.maxCapturedFrames = maxCapturedFrames; + this.applyConfigAsync = applyConfigAsync; } @Override @@ -108,7 +112,11 @@ public void handleException(Throwable t, AgentSpan span) { exceptionProbeManager.createProbesForException( throwable.getStackTrace(), chainedExceptionIdx); if (creationResult.probesCreated > 0) { - AgentTaskScheduler.INSTANCE.execute(() -> applyExceptionConfiguration(fingerprint)); + if (applyConfigAsync) { + AgentTaskScheduler.INSTANCE.execute(() -> applyExceptionConfiguration(fingerprint)); + } else { + applyExceptionConfiguration(fingerprint); + } break; } else { if (LOGGER.isDebugEnabled()) { diff --git a/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/exception/DefaultExceptionDebuggerTest.java b/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/exception/DefaultExceptionDebuggerTest.java index a399fee533b..6928bb4b3c4 100644 --- a/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/exception/DefaultExceptionDebuggerTest.java +++ b/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/exception/DefaultExceptionDebuggerTest.java @@ -65,10 +65,10 @@ public void setUp() { classNameFiltering = new ClassNameFiltering( new HashSet<>(singletonList("com.datadog.debugger.exception.ThirdPartyCode"))); + Config config = createConfig(); exceptionDebugger = - new DefaultExceptionDebugger( - configurationUpdater, classNameFiltering, Duration.ofHours(1), 100, 3); - listener = new TestSnapshotListener(createConfig(), mock(ProbeStatusSink.class)); + new DefaultExceptionDebugger(configurationUpdater, classNameFiltering, config); + listener = new TestSnapshotListener(config, mock(ProbeStatusSink.class)); DebuggerAgentHelper.injectSink(listener); } @@ -275,11 +275,24 @@ public void nestedExceptionFullThirdParty() { public void filteringOutErrors() { ExceptionProbeManager manager = mock(ExceptionProbeManager.class); exceptionDebugger = - new DefaultExceptionDebugger(manager, configurationUpdater, classNameFiltering, 100, 3); + new DefaultExceptionDebugger( + manager, configurationUpdater, classNameFiltering, 100, 3, true); exceptionDebugger.handleException(new AssertionError("test"), mock(AgentSpan.class)); verify(manager, times(0)).isAlreadyInstrumented(any()); } + @Test + public void syncConfig() { + RuntimeException exception = new RuntimeException("test"); + String fingerprint = Fingerprinter.fingerprint(exception, classNameFiltering); + AgentSpan span = mock(AgentSpan.class); + exceptionDebugger.handleException(exception, span); + // instrumentation should be applied synchronously + assertTrue(exceptionDebugger.getExceptionProbeManager().isAlreadyInstrumented(fingerprint)); + exceptionDebugger.handleException(exception, span); + verify(configurationUpdater).accept(eq(ConfigurationAcceptor.Source.EXCEPTION), any()); + } + private Object recordTags(InvocationOnMock invocationOnMock) { Object[] args = invocationOnMock.getArguments(); String key = (String) args[0]; @@ -414,6 +427,9 @@ public static Config createConfig() { .thenReturn("http://localhost:8126/debugger/v1/input"); when(config.getFinalDebuggerSymDBUrl()).thenReturn("http://localhost:8126/symdb/v1/input"); when(config.getDynamicInstrumentationUploadBatchSize()).thenReturn(100); + when(config.getDebuggerExceptionCaptureInterval()).thenReturn(3600); + when(config.getDebuggerMaxExceptionPerSecond()).thenReturn(100); + when(config.getDebuggerExceptionMaxCapturedFrames()).thenReturn(3); return config; } } diff --git a/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/exception/ExceptionProbeInstrumentationTest.java b/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/exception/ExceptionProbeInstrumentationTest.java index f5266fc8d5f..61ea13f4bc9 100644 --- a/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/exception/ExceptionProbeInstrumentationTest.java +++ b/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/exception/ExceptionProbeInstrumentationTest.java @@ -383,7 +383,7 @@ private TestSnapshotListener setupExceptionDebugging( DebuggerContext.initValueSerializer(new JsonSnapshotSerializer()); DefaultExceptionDebugger exceptionDebugger = new DefaultExceptionDebugger( - exceptionProbeManager, configurationUpdater, classNameFiltering, 100, 3); + exceptionProbeManager, configurationUpdater, classNameFiltering, 100, 3, true); DebuggerContext.initExceptionDebugger(exceptionDebugger); configurationUpdater.accept(REMOTE_CONFIG, definitions); return listener; diff --git a/dd-trace-api/src/main/java/datadog/trace/api/ConfigDefaults.java b/dd-trace-api/src/main/java/datadog/trace/api/ConfigDefaults.java index ab052ca6707..0a039ed2cce 100644 --- a/dd-trace-api/src/main/java/datadog/trace/api/ConfigDefaults.java +++ b/dd-trace-api/src/main/java/datadog/trace/api/ConfigDefaults.java @@ -206,6 +206,7 @@ public final class ConfigDefaults { static final boolean DEFAULT_DEBUGGER_EXCEPTION_CAPTURE_INTERMEDIATE_SPANS_ENABLED = true; static final int DEFAULT_DEBUGGER_EXCEPTION_MAX_CAPTURED_FRAMES = 3; static final int DEFAULT_DEBUGGER_EXCEPTION_CAPTURE_INTERVAL_SECONDS = 60 * 60; + static final boolean DEFAULT_DEBUGGER_EXCEPTION_ASYNC_CONFIG = true; static final boolean DEFAULT_DISTRIBUTED_DEBUGGER_ENABLED = false; static final boolean DEFAULT_DEBUGGER_SOURCE_FILE_TRACKING_ENABLED = true; diff --git a/dd-trace-api/src/main/java/datadog/trace/api/config/DebuggerConfig.java b/dd-trace-api/src/main/java/datadog/trace/api/config/DebuggerConfig.java index 536821c9a25..f9695695ee0 100644 --- a/dd-trace-api/src/main/java/datadog/trace/api/config/DebuggerConfig.java +++ b/dd-trace-api/src/main/java/datadog/trace/api/config/DebuggerConfig.java @@ -58,6 +58,8 @@ public final class DebuggerConfig { "exception.replay.capture.max.frames"; public static final String DEBUGGER_EXCEPTION_CAPTURE_INTERVAL_SECONDS = "exception.replay.capture.interval.seconds"; + public static final String DEBUGGER_EXCEPTION_ASYNC_CONFIG = + "internal.exception.replay.async.config"; public static final String DEBUGGER_EXCEPTION_CAPTURE_INTERMEDIATE_SPANS_ENABLED = "exception.replay.capture.intermediate.spans.enabled"; public static final String DISTRIBUTED_DEBUGGER_ENABLED = "distributed.debugger.enabled"; diff --git a/internal-api/src/main/java/datadog/trace/api/Config.java b/internal-api/src/main/java/datadog/trace/api/Config.java index dc1b70139d1..8c86af37e64 100644 --- a/internal-api/src/main/java/datadog/trace/api/Config.java +++ b/internal-api/src/main/java/datadog/trace/api/Config.java @@ -54,6 +54,7 @@ import static datadog.trace.api.ConfigDefaults.DEFAULT_DB_CLIENT_HOST_SPLIT_BY_INSTANCE_TYPE_SUFFIX; import static datadog.trace.api.ConfigDefaults.DEFAULT_DB_DBM_PROPAGATION_MODE_MODE; import static datadog.trace.api.ConfigDefaults.DEFAULT_DB_DBM_TRACE_PREPARED_STATEMENTS; +import static datadog.trace.api.ConfigDefaults.DEFAULT_DEBUGGER_EXCEPTION_ASYNC_CONFIG; import static datadog.trace.api.ConfigDefaults.DEFAULT_DEBUGGER_EXCEPTION_CAPTURE_INTERMEDIATE_SPANS_ENABLED; import static datadog.trace.api.ConfigDefaults.DEFAULT_DEBUGGER_EXCEPTION_CAPTURE_INTERVAL_SECONDS; import static datadog.trace.api.ConfigDefaults.DEFAULT_DEBUGGER_EXCEPTION_ENABLED; @@ -277,6 +278,7 @@ import static datadog.trace.api.config.CrashTrackingConfig.CRASH_TRACKING_TAGS; import static datadog.trace.api.config.CwsConfig.CWS_ENABLED; import static datadog.trace.api.config.CwsConfig.CWS_TLS_REFRESH; +import static datadog.trace.api.config.DebuggerConfig.DEBUGGER_EXCEPTION_ASYNC_CONFIG; import static datadog.trace.api.config.DebuggerConfig.DEBUGGER_EXCEPTION_CAPTURE_INTERMEDIATE_SPANS_ENABLED; import static datadog.trace.api.config.DebuggerConfig.DEBUGGER_EXCEPTION_CAPTURE_INTERVAL_SECONDS; import static datadog.trace.api.config.DebuggerConfig.DEBUGGER_EXCEPTION_CAPTURE_MAX_FRAMES; @@ -301,6 +303,7 @@ import static datadog.trace.api.config.DebuggerConfig.DYNAMIC_INSTRUMENTATION_REDACTED_IDENTIFIERS; import static datadog.trace.api.config.DebuggerConfig.DYNAMIC_INSTRUMENTATION_REDACTED_TYPES; import static datadog.trace.api.config.DebuggerConfig.DYNAMIC_INSTRUMENTATION_REDACTION_EXCLUDED_IDENTIFIERS; +import static datadog.trace.api.config.DebuggerConfig.DYNAMIC_INSTRUMENTATION_SNAPSHOT_URL; import static datadog.trace.api.config.DebuggerConfig.DYNAMIC_INSTRUMENTATION_UPLOAD_BATCH_SIZE; import static datadog.trace.api.config.DebuggerConfig.DYNAMIC_INSTRUMENTATION_UPLOAD_FLUSH_INTERVAL; import static datadog.trace.api.config.DebuggerConfig.DYNAMIC_INSTRUMENTATION_UPLOAD_INTERVAL_SECONDS; @@ -1039,6 +1042,7 @@ public static String getHostName() { private final boolean DBMTracePreparedStatements; private final boolean dynamicInstrumentationEnabled; + private final String dynamicInstrumentationSnapshotUrl; private final int dynamicInstrumentationUploadTimeout; private final int dynamicInstrumentationUploadFlushInterval; private final boolean dynamicInstrumentationClassFileDumpEnabled; @@ -1067,6 +1071,7 @@ public static String getHostName() { private final boolean debuggerExceptionCaptureIntermediateSpansEnabled; private final int debuggerExceptionMaxCapturedFrames; private final int debuggerExceptionCaptureInterval; + private final boolean debuggerExceptionAsyncConfig; private final boolean debuggerCodeOriginEnabled; private final int debuggerCodeOriginMaxUserFrames; private final boolean distributedDebuggerEnabled; @@ -2318,6 +2323,8 @@ PROFILING_DATADOG_PROFILER_ENABLED, isDatadogProfilerSafeInCurrentEnvironment()) dynamicInstrumentationEnabled = configProvider.getBoolean( DYNAMIC_INSTRUMENTATION_ENABLED, DEFAULT_DYNAMIC_INSTRUMENTATION_ENABLED); + dynamicInstrumentationSnapshotUrl = + configProvider.getString(DYNAMIC_INSTRUMENTATION_SNAPSHOT_URL); distributedDebuggerEnabled = configProvider.getBoolean( DISTRIBUTED_DEBUGGER_ENABLED, DEFAULT_DISTRIBUTED_DEBUGGER_ENABLED); @@ -2427,6 +2434,9 @@ PROFILING_DATADOG_PROFILER_ENABLED, isDatadogProfilerSafeInCurrentEnvironment()) configProvider.getInteger( DEBUGGER_EXCEPTION_CAPTURE_INTERVAL_SECONDS, DEFAULT_DEBUGGER_EXCEPTION_CAPTURE_INTERVAL_SECONDS); + debuggerExceptionAsyncConfig = + configProvider.getBoolean( + DEBUGGER_EXCEPTION_ASYNC_CONFIG, DEFAULT_DEBUGGER_EXCEPTION_ASYNC_CONFIG); debuggerSourceFileTrackingEnabled = configProvider.getBoolean( DEBUGGER_SOURCE_FILE_TRACKING_ENABLED, DEFAULT_DEBUGGER_SOURCE_FILE_TRACKING_ENABLED); @@ -4054,6 +4064,10 @@ public int getDebuggerExceptionCaptureInterval() { return debuggerExceptionCaptureInterval; } + public boolean isDebuggerExceptionAsyncConfig() { + return debuggerExceptionAsyncConfig; + } + public boolean isDebuggerCodeOriginEnabled() { return debuggerCodeOriginEnabled; } @@ -4092,6 +4106,9 @@ private String getFinalDebuggerBaseUrl() { } public String getFinalDebuggerSnapshotUrl() { + if (dynamicInstrumentationSnapshotUrl != null && !dynamicInstrumentationSnapshotUrl.isEmpty()) { + return dynamicInstrumentationSnapshotUrl; + } return getFinalDebuggerBaseUrl() + "/debugger/v1/input"; }