From 2c219fdca830101ea2f6abd316874bec6d5fd519 Mon Sep 17 00:00:00 2001 From: Daniel Mohedano Date: Fri, 4 Jul 2025 12:22:57 +0200 Subject: [PATCH 1/8] add `di_enabled` to settings response --- .../civisibility/config/CiVisibilitySettings.java | 11 +++++++++++ .../config/ConfigurationApiImplTest.groovy | 8 ++++---- .../trace/civisibility/config/settings-response.ftl | 1 + 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/config/CiVisibilitySettings.java b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/config/CiVisibilitySettings.java index bdce3fb02b8..13e254496ca 100644 --- a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/config/CiVisibilitySettings.java +++ b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/config/CiVisibilitySettings.java @@ -17,6 +17,7 @@ public class CiVisibilitySettings { false, false, false, + false, EarlyFlakeDetectionSettings.DEFAULT, TestManagementSettings.DEFAULT, null); @@ -28,6 +29,7 @@ public class CiVisibilitySettings { private final boolean flakyTestRetriesEnabled; private final boolean impactedTestsDetectionEnabled; private final boolean knownTestsEnabled; + private final boolean failedTestReplayEnabled; private final EarlyFlakeDetectionSettings earlyFlakeDetectionSettings; private final TestManagementSettings testManagementSettings; @Nullable private final String defaultBranch; @@ -40,6 +42,7 @@ public class CiVisibilitySettings { boolean flakyTestRetriesEnabled, boolean impactedTestsDetectionEnabled, boolean knownTestsEnabled, + boolean failedTestReplayEnabled, EarlyFlakeDetectionSettings earlyFlakeDetectionSettings, TestManagementSettings testManagementSettings, @Nullable String defaultBranch) { @@ -50,6 +53,7 @@ public class CiVisibilitySettings { this.flakyTestRetriesEnabled = flakyTestRetriesEnabled; this.impactedTestsDetectionEnabled = impactedTestsDetectionEnabled; this.knownTestsEnabled = knownTestsEnabled; + this.failedTestReplayEnabled = failedTestReplayEnabled; this.earlyFlakeDetectionSettings = earlyFlakeDetectionSettings; this.testManagementSettings = testManagementSettings; this.defaultBranch = defaultBranch; @@ -83,6 +87,10 @@ public boolean isKnownTestsEnabled() { return knownTestsEnabled; } + public boolean isFailedTestReplayEnabled() { + return failedTestReplayEnabled; + } + public EarlyFlakeDetectionSettings getEarlyFlakeDetectionSettings() { return earlyFlakeDetectionSettings; } @@ -112,6 +120,7 @@ public boolean equals(Object o) { && flakyTestRetriesEnabled == that.flakyTestRetriesEnabled && impactedTestsDetectionEnabled == that.impactedTestsDetectionEnabled && knownTestsEnabled == that.knownTestsEnabled + && failedTestReplayEnabled == that.failedTestReplayEnabled && Objects.equals(earlyFlakeDetectionSettings, that.earlyFlakeDetectionSettings) && Objects.equals(testManagementSettings, that.testManagementSettings) && Objects.equals(defaultBranch, that.defaultBranch); @@ -127,6 +136,7 @@ public int hashCode() { flakyTestRetriesEnabled, impactedTestsDetectionEnabled, knownTestsEnabled, + failedTestReplayEnabled, earlyFlakeDetectionSettings, testManagementSettings, defaultBranch); @@ -154,6 +164,7 @@ public CiVisibilitySettings fromJson(Map json) { getBoolean(json, "flaky_test_retries_enabled", false), getBoolean(json, "impacted_tests_enabled", false), getBoolean(json, "known_tests_enabled", false), + getBoolean(json, "di_enabled", false), EarlyFlakeDetectionSettings.JsonAdapter.INSTANCE.fromJson( (Map) json.get("early_flake_detection")), TestManagementSettings.JsonAdapter.INSTANCE.fromJson( diff --git a/dd-java-agent/agent-ci-visibility/src/test/groovy/datadog/trace/civisibility/config/ConfigurationApiImplTest.groovy b/dd-java-agent/agent-ci-visibility/src/test/groovy/datadog/trace/civisibility/config/ConfigurationApiImplTest.groovy index 4cb3ee89c5a..f8e71d07528 100644 --- a/dd-java-agent/agent-ci-visibility/src/test/groovy/datadog/trace/civisibility/config/ConfigurationApiImplTest.groovy +++ b/dd-java-agent/agent-ci-visibility/src/test/groovy/datadog/trace/civisibility/config/ConfigurationApiImplTest.groovy @@ -55,10 +55,10 @@ class ConfigurationApiImplTest extends Specification { where: agentless | compression | expectedSettings - false | false | new CiVisibilitySettings(false, false, false, false, false, false, false, EarlyFlakeDetectionSettings.DEFAULT, TestManagementSettings.DEFAULT, null) - false | true | new CiVisibilitySettings(true, true, true, true, true, true, true, EarlyFlakeDetectionSettings.DEFAULT, TestManagementSettings.DEFAULT, "main") - true | false | new CiVisibilitySettings(false, true, false, true, false, true, false, new EarlyFlakeDetectionSettings(true, [new ExecutionsByDuration(1000, 3)], 10), new TestManagementSettings(true, 10), "master") - true | true | new CiVisibilitySettings(false, false, true, true, false, false, true, new EarlyFlakeDetectionSettings(true, [new ExecutionsByDuration(5000, 3), new ExecutionsByDuration(120000, 2)], 10), new TestManagementSettings(true, 20), "prod") + false | false | new CiVisibilitySettings(false, false, false, false, false, false, false, false, EarlyFlakeDetectionSettings.DEFAULT, TestManagementSettings.DEFAULT, null) + false | true | new CiVisibilitySettings(true, true, true, true, true, true, true, true, EarlyFlakeDetectionSettings.DEFAULT, TestManagementSettings.DEFAULT, "main") + true | false | new CiVisibilitySettings(false, true, false, true, false, true, false, true, new EarlyFlakeDetectionSettings(true, [new ExecutionsByDuration(1000, 3)], 10), new TestManagementSettings(true, 10), "master") + true | true | new CiVisibilitySettings(false, false, true, true, false, false, true, false, new EarlyFlakeDetectionSettings(true, [new ExecutionsByDuration(5000, 3), new ExecutionsByDuration(120000, 2)], 10), new TestManagementSettings(true, 20), "prod") } def "test skippable tests request"() { diff --git a/dd-java-agent/agent-ci-visibility/src/test/resources/datadog/trace/civisibility/config/settings-response.ftl b/dd-java-agent/agent-ci-visibility/src/test/resources/datadog/trace/civisibility/config/settings-response.ftl index f21b821434e..43f79b251b8 100644 --- a/dd-java-agent/agent-ci-visibility/src/test/resources/datadog/trace/civisibility/config/settings-response.ftl +++ b/dd-java-agent/agent-ci-visibility/src/test/resources/datadog/trace/civisibility/config/settings-response.ftl @@ -10,6 +10,7 @@ "flaky_test_retries_enabled": ${settings.flakyTestRetriesEnabled?c}, "impacted_tests_enabled": ${settings.impactedTestsDetectionEnabled?c}, "known_tests_enabled": ${settings.knownTestsEnabled?c}, + "di_enabled": ${settings.failedTestReplayEnabled?c}, <#if settings.defaultBranch??> "default_branch": "${settings.defaultBranch}", From a911ae043a1ee9fa5a37d5e6013d8f865dd1dc54 Mon Sep 17 00:00:00 2001 From: Daniel Mohedano Date: Fri, 4 Jul 2025 12:25:26 +0200 Subject: [PATCH 2/8] add FTR related metrics --- .../config/ConfigurationApiImpl.java | 2 ++ .../domain/AbstractTestSession.java | 3 +- .../trace/civisibility/domain/TestImpl.java | 1 + .../telemetry/CiVisibilityCountMetric.java | 6 +++- .../tag/FailedTestReplayEnabled.java | 32 +++++++++++++++++++ 5 files changed, 42 insertions(+), 2 deletions(-) create mode 100644 internal-api/src/main/java/datadog/trace/api/civisibility/telemetry/tag/FailedTestReplayEnabled.java diff --git a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/config/ConfigurationApiImpl.java b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/config/ConfigurationApiImpl.java index 76dcc1ece2e..16ba542e2a3 100644 --- a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/config/ConfigurationApiImpl.java +++ b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/config/ConfigurationApiImpl.java @@ -17,6 +17,7 @@ import datadog.trace.api.civisibility.telemetry.CiVisibilityMetricCollector; import datadog.trace.api.civisibility.telemetry.tag.CoverageEnabled; import datadog.trace.api.civisibility.telemetry.tag.EarlyFlakeDetectionEnabled; +import datadog.trace.api.civisibility.telemetry.tag.FailedTestReplayEnabled; import datadog.trace.api.civisibility.telemetry.tag.FlakyTestRetriesEnabled; import datadog.trace.api.civisibility.telemetry.tag.ImpactedTestsDetectionEnabled; import datadog.trace.api.civisibility.telemetry.tag.ItrEnabled; @@ -156,6 +157,7 @@ public CiVisibilitySettings getSettings(TracerEnvironment tracerEnvironment) thr settings.isKnownTestsEnabled() ? KnownTestsEnabled.TRUE : null, settings.isImpactedTestsDetectionEnabled() ? ImpactedTestsDetectionEnabled.TRUE : null, settings.getTestManagementSettings().isEnabled() ? TestManagementEnabled.TRUE : null, + settings.isFailedTestReplayEnabled() ? FailedTestReplayEnabled.SettingsMetric.TRUE : null, settings.isGitUploadRequired() ? RequireGit.TRUE : null); return settings; diff --git a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/domain/AbstractTestSession.java b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/domain/AbstractTestSession.java index 51e9392732b..2e191940f52 100644 --- a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/domain/AbstractTestSession.java +++ b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/domain/AbstractTestSession.java @@ -130,7 +130,8 @@ public AbstractTestSession( config.isAgentlessLogSubmissionEnabled() ? AgentlessLogSubmissionEnabled.TRUE : null, CIConstants.FAIL_FAST_TEST_ORDER.equalsIgnoreCase(config.getCiVisibilityTestOrder()) ? FailFastTestOrderEnabled.TRUE - : null); + : null, + null); if (instrumentationType == InstrumentationType.MANUAL_API) { metricCollector.add(CiVisibilityCountMetric.MANUAL_API_EVENTS, 1, EventType.SESSION); diff --git a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/domain/TestImpl.java b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/domain/TestImpl.java index 47042b7effa..13e9cfb2940 100644 --- a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/domain/TestImpl.java +++ b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/domain/TestImpl.java @@ -307,6 +307,7 @@ public void end(@Nullable Long endTime) { span.getTag(Tags.TEST_IS_RETRY) != null ? IsRetry.TRUE : null, span.getTag(Tags.TEST_HAS_FAILED_ALL_RETRIES) != null ? HasFailedAllRetries.TRUE : null, retryReason instanceof TagValue ? (TagValue) retryReason : null, + null, span.getTag(Tags.TEST_IS_RUM_ACTIVE) != null ? IsRum.TRUE : null, CIConstants.SELENIUM_BROWSER_DRIVER.equals(span.getTag(Tags.TEST_BROWSER_DRIVER)) ? BrowserDriver.SELENIUM diff --git a/internal-api/src/main/java/datadog/trace/api/civisibility/telemetry/CiVisibilityCountMetric.java b/internal-api/src/main/java/datadog/trace/api/civisibility/telemetry/CiVisibilityCountMetric.java index 868ecde90b8..d214f775ac8 100644 --- a/internal-api/src/main/java/datadog/trace/api/civisibility/telemetry/CiVisibilityCountMetric.java +++ b/internal-api/src/main/java/datadog/trace/api/civisibility/telemetry/CiVisibilityCountMetric.java @@ -13,6 +13,7 @@ import datadog.trace.api.civisibility.telemetry.tag.EventType; import datadog.trace.api.civisibility.telemetry.tag.ExitCode; import datadog.trace.api.civisibility.telemetry.tag.FailFastTestOrderEnabled; +import datadog.trace.api.civisibility.telemetry.tag.FailedTestReplayEnabled; import datadog.trace.api.civisibility.telemetry.tag.FlakyTestRetriesEnabled; import datadog.trace.api.civisibility.telemetry.tag.GitProviderDiscrepant; import datadog.trace.api.civisibility.telemetry.tag.GitProviderExpected; @@ -53,7 +54,8 @@ public enum CiVisibilityCountMetric { Provider.class, AutoInjected.class, AgentlessLogSubmissionEnabled.class, - FailFastTestOrderEnabled.class), + FailFastTestOrderEnabled.class, + FailedTestReplayEnabled.SessionMetric.class), /** The number of events created */ EVENT_CREATED( "event_created", @@ -84,6 +86,7 @@ public enum CiVisibilityCountMetric { IsRetry.class, HasFailedAllRetries.class, RetryReason.class, + FailedTestReplayEnabled.TestMetric.class, IsRum.class, BrowserDriver.class), /** The number of successfully collected code coverages that are empty */ @@ -138,6 +141,7 @@ public enum CiVisibilityCountMetric { ImpactedTestsDetectionEnabled.class, KnownTestsEnabled.class, TestManagementEnabled.class, + FailedTestReplayEnabled.SettingsMetric.class, RequireGit.class), /** The number of requests sent to the itr skippable tests endpoint */ ITR_SKIPPABLE_TESTS_REQUEST("itr_skippable_tests.request", RequestCompressed.class), diff --git a/internal-api/src/main/java/datadog/trace/api/civisibility/telemetry/tag/FailedTestReplayEnabled.java b/internal-api/src/main/java/datadog/trace/api/civisibility/telemetry/tag/FailedTestReplayEnabled.java new file mode 100644 index 00000000000..3bca7092725 --- /dev/null +++ b/internal-api/src/main/java/datadog/trace/api/civisibility/telemetry/tag/FailedTestReplayEnabled.java @@ -0,0 +1,32 @@ +package datadog.trace.api.civisibility.telemetry.tag; + +import datadog.trace.api.civisibility.telemetry.TagValue; + +public abstract class FailedTestReplayEnabled { + public enum SettingsMetric implements TagValue { + TRUE; + + @Override + public String asString() { + return "failed_test_replay_enabled:true"; + } + } + + public enum SessionMetric implements TagValue { + TRUE; + + @Override + public String asString() { + return "has_failed_test_replay:true"; + } + } + + public enum TestMetric implements TagValue { + TRUE; + + @Override + public String asString() { + return "is_failed_test_replay_enabled:true"; + } + } +} From d20159e24e33c2cce5cb2b6a488cdc54ebe74840 Mon Sep 17 00:00:00 2001 From: Daniel Mohedano Date: Fri, 4 Jul 2025 12:58:49 +0200 Subject: [PATCH 3/8] add FTR to execution settings --- .../civisibility/config/ExecutionSettings.java | 18 +++++++++++++++++- .../config/ExecutionSettingsFactoryImpl.java | 12 ++++++++++-- .../config/ExecutionSettingsTest.groovy | 7 +++++-- .../trace/api/config/CiVisibilityConfig.java | 1 + .../main/java/datadog/trace/api/Config.java | 8 ++++++++ 5 files changed, 41 insertions(+), 5 deletions(-) diff --git a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/config/ExecutionSettings.java b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/config/ExecutionSettings.java index 4d93dcf5d38..c20ce5d15db 100644 --- a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/config/ExecutionSettings.java +++ b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/config/ExecutionSettings.java @@ -26,6 +26,7 @@ public class ExecutionSettings { false, false, false, + false, EarlyFlakeDetectionSettings.DEFAULT, TestManagementSettings.DEFAULT, null, @@ -43,6 +44,7 @@ public class ExecutionSettings { private final boolean testSkippingEnabled; private final boolean flakyTestRetriesEnabled; private final boolean impactedTestsDetectionEnabled; + private final boolean failedTestReplayEnabled; @Nonnull private final EarlyFlakeDetectionSettings earlyFlakeDetectionSettings; @Nonnull private final TestManagementSettings testManagementSettings; @Nullable private final String itrCorrelationId; @@ -58,6 +60,7 @@ public ExecutionSettings( boolean testSkippingEnabled, boolean flakyTestRetriesEnabled, boolean impactedTestsDetectionEnabled, + boolean failedTestReplayEnabled, @Nonnull EarlyFlakeDetectionSettings earlyFlakeDetectionSettings, @Nonnull TestManagementSettings testManagementSettings, @Nullable String itrCorrelationId, @@ -74,6 +77,7 @@ public ExecutionSettings( this.testSkippingEnabled = testSkippingEnabled; this.flakyTestRetriesEnabled = flakyTestRetriesEnabled; this.impactedTestsDetectionEnabled = impactedTestsDetectionEnabled; + this.failedTestReplayEnabled = failedTestReplayEnabled; this.earlyFlakeDetectionSettings = earlyFlakeDetectionSettings; this.testManagementSettings = testManagementSettings; this.itrCorrelationId = itrCorrelationId; @@ -110,6 +114,7 @@ private ExecutionSettings( boolean testSkippingEnabled, boolean flakyTestRetriesEnabled, boolean impactedTestsDetectionEnabled, + boolean failedTestReplayEnabled, @Nonnull EarlyFlakeDetectionSettings earlyFlakeDetectionSettings, @Nonnull TestManagementSettings testManagementSettings, @Nullable String itrCorrelationId, @@ -123,6 +128,7 @@ private ExecutionSettings( this.testSkippingEnabled = testSkippingEnabled; this.flakyTestRetriesEnabled = flakyTestRetriesEnabled; this.impactedTestsDetectionEnabled = impactedTestsDetectionEnabled; + this.failedTestReplayEnabled = failedTestReplayEnabled; this.earlyFlakeDetectionSettings = earlyFlakeDetectionSettings; this.testManagementSettings = testManagementSettings; this.itrCorrelationId = itrCorrelationId; @@ -157,6 +163,10 @@ public boolean isImpactedTestsDetectionEnabled() { return impactedTestsDetectionEnabled; } + public boolean isFailedTestReplayEnabled() { + return failedTestReplayEnabled; + } + @Nonnull public EarlyFlakeDetectionSettings getEarlyFlakeDetectionSettings() { return earlyFlakeDetectionSettings; @@ -243,6 +253,7 @@ public boolean equals(Object o) { && testSkippingEnabled == that.testSkippingEnabled && flakyTestRetriesEnabled == that.flakyTestRetriesEnabled && impactedTestsDetectionEnabled == that.impactedTestsDetectionEnabled + && failedTestReplayEnabled == that.failedTestReplayEnabled && Objects.equals(earlyFlakeDetectionSettings, that.earlyFlakeDetectionSettings) && Objects.equals(testManagementSettings, that.testManagementSettings) && Objects.equals(itrCorrelationId, that.itrCorrelationId) @@ -261,6 +272,7 @@ public int hashCode() { testSkippingEnabled, flakyTestRetriesEnabled, impactedTestsDetectionEnabled, + failedTestReplayEnabled, earlyFlakeDetectionSettings, testManagementSettings, itrCorrelationId, @@ -278,6 +290,7 @@ public static class Serializer { private static final int TEST_SKIPPING_ENABLED_FLAG = 4; private static final int FLAKY_TEST_RETRIES_ENABLED_FLAG = 8; private static final int IMPACTED_TESTS_DETECTION_ENABLED_FLAG = 16; + private static final int FAILED_TEST_REPLAY_ENABLED_FLAG = 32; public static ByteBuffer serialize(ExecutionSettings settings) { datadog.trace.civisibility.ipc.serialization.Serializer s = @@ -291,7 +304,8 @@ public static ByteBuffer serialize(ExecutionSettings settings) { | (settings.flakyTestRetriesEnabled ? FLAKY_TEST_RETRIES_ENABLED_FLAG : 0) | (settings.impactedTestsDetectionEnabled ? IMPACTED_TESTS_DETECTION_ENABLED_FLAG - : 0)); + : 0) + | (settings.failedTestReplayEnabled ? FAILED_TEST_REPLAY_ENABLED_FLAG : 0)); s.write(flags); EarlyFlakeDetectionSettings.Serializer.serialize(s, settings.earlyFlakeDetectionSettings); @@ -330,6 +344,7 @@ public static ExecutionSettings deserialize(ByteBuffer buffer) { boolean testSkippingEnabled = (flags & TEST_SKIPPING_ENABLED_FLAG) != 0; boolean flakyTestRetriesEnabled = (flags & FLAKY_TEST_RETRIES_ENABLED_FLAG) != 0; boolean impactedTestsDetectionEnabled = (flags & IMPACTED_TESTS_DETECTION_ENABLED_FLAG) != 0; + boolean failedTestReplayEnabled = (flags & FAILED_TEST_REPLAY_ENABLED_FLAG) != 0; EarlyFlakeDetectionSettings earlyFlakeDetectionSettings = EarlyFlakeDetectionSettings.Serializer.deserialize(buffer); @@ -372,6 +387,7 @@ public static ExecutionSettings deserialize(ByteBuffer buffer) { testSkippingEnabled, flakyTestRetriesEnabled, impactedTestsDetectionEnabled, + failedTestReplayEnabled, earlyFlakeDetectionSettings, testManagementSettings, itrCorrelationId, diff --git a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/config/ExecutionSettingsFactoryImpl.java b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/config/ExecutionSettingsFactoryImpl.java index 7d50d1a573b..eacedca6178 100644 --- a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/config/ExecutionSettingsFactoryImpl.java +++ b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/config/ExecutionSettingsFactoryImpl.java @@ -176,6 +176,11 @@ private Map doCreate( settings, CiVisibilitySettings::isKnownTestsEnabled, Config::isCiVisibilityKnownTestsRequestEnabled); + boolean failedTestReplayEnabled = + isFeatureEnabled( + settings, + CiVisibilitySettings::isFailedTestReplayEnabled, + Config::isCiVisibilityFailedTestReplayEnabled); TestManagementSettings testManagementSettings = getTestManagementSettings(settings); @@ -188,7 +193,8 @@ private Map doCreate( + "Impacted tests detection - {},\n" + "Known tests marking - {},\n" + "Auto test retries - {},\n" - + "Test Management - {}", + + "Test Management - {},\n" + + "Failed Test Replay - {}", repositoryRoot, tracerEnvironment.getConfigurations().getRuntimeName(), tracerEnvironment.getConfigurations().getRuntimeVersion(), @@ -200,7 +206,8 @@ private Map doCreate( impactedTestsEnabled, knownTestsRequest, flakyTestRetriesEnabled, - testManagementSettings.isEnabled()); + testManagementSettings.isEnabled(), + failedTestReplayEnabled); Future skippableTestsFuture = executor.submit(() -> getSkippableTests(tracerEnvironment, itrEnabled)); @@ -252,6 +259,7 @@ private Map doCreate( testSkippingEnabled, flakyTestRetriesEnabled, impactedTestsEnabled, + failedTestReplayEnabled, earlyFlakeDetectionEnabled ? settings.getEarlyFlakeDetectionSettings() : EarlyFlakeDetectionSettings.DEFAULT, diff --git a/dd-java-agent/agent-ci-visibility/src/test/groovy/datadog/trace/civisibility/config/ExecutionSettingsTest.groovy b/dd-java-agent/agent-ci-visibility/src/test/groovy/datadog/trace/civisibility/config/ExecutionSettingsTest.groovy index cd9a84e3fd8..2af232bfb1c 100644 --- a/dd-java-agent/agent-ci-visibility/src/test/groovy/datadog/trace/civisibility/config/ExecutionSettingsTest.groovy +++ b/dd-java-agent/agent-ci-visibility/src/test/groovy/datadog/trace/civisibility/config/ExecutionSettingsTest.groovy @@ -1,7 +1,6 @@ package datadog.trace.civisibility.config import datadog.trace.api.civisibility.CIConstants -import datadog.trace.api.civisibility.config.LibraryCapability import datadog.trace.api.civisibility.config.TestFQN import datadog.trace.api.civisibility.config.TestIdentifier import datadog.trace.api.civisibility.config.TestMetadata @@ -9,7 +8,6 @@ import datadog.trace.api.config.CiVisibilityConfig import datadog.trace.civisibility.diff.LineDiff import datadog.trace.test.util.DDSpecification -import java.util.stream.Collectors import static datadog.trace.civisibility.TestUtils.lines @@ -31,6 +29,7 @@ class ExecutionSettingsTest extends DDSpecification { false, false, false, + false, EarlyFlakeDetectionSettings.DEFAULT, TestManagementSettings.DEFAULT, null, @@ -49,6 +48,7 @@ class ExecutionSettingsTest extends DDSpecification { false, true, true, + true, new EarlyFlakeDetectionSettings(true, [], 10), new TestManagementSettings(true, 20), "", @@ -68,6 +68,7 @@ class ExecutionSettingsTest extends DDSpecification { true, false, true, + false, new EarlyFlakeDetectionSettings(true, [new ExecutionsByDuration(10, 20)], 10), new TestManagementSettings(true, 20), "itrCorrelationId", @@ -91,6 +92,7 @@ class ExecutionSettingsTest extends DDSpecification { true, true, true, + true, new EarlyFlakeDetectionSettings(true, [new ExecutionsByDuration(10, 20), new ExecutionsByDuration(30, 40)], 10), new TestManagementSettings(true, 20), "itrCorrelationId", @@ -127,6 +129,7 @@ class ExecutionSettingsTest extends DDSpecification { settingsEnabled, settingsEnabled, settingsEnabled, + settingsEnabled, earlyFlakeDetectionSettings, testManagementSettings, null, diff --git a/dd-trace-api/src/main/java/datadog/trace/api/config/CiVisibilityConfig.java b/dd-trace-api/src/main/java/datadog/trace/api/config/CiVisibilityConfig.java index ad93aa9dfc7..e468bcf6761 100644 --- a/dd-trace-api/src/main/java/datadog/trace/api/config/CiVisibilityConfig.java +++ b/dd-trace-api/src/main/java/datadog/trace/api/config/CiVisibilityConfig.java @@ -82,6 +82,7 @@ public final class CiVisibilityConfig { public static final String TEST_MANAGEMENT_ENABLED = "test.management.enabled"; public static final String TEST_MANAGEMENT_ATTEMPT_TO_FIX_RETRIES = "test.management.attempt.to.fix.retries"; + public static final String TEST_FAILED_TEST_REPLAY_ENABLED = "test.failed.test.replay.enabled"; /* Git PR info */ public static final String GIT_PULL_REQUEST_BASE_BRANCH = "git.pull.request.base.branch"; 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 8f1c06837ce..d0886af9fc2 100644 --- a/internal-api/src/main/java/datadog/trace/api/Config.java +++ b/internal-api/src/main/java/datadog/trace/api/Config.java @@ -266,6 +266,7 @@ import static datadog.trace.api.config.CiVisibilityConfig.GIT_COMMIT_HEAD_SHA; import static datadog.trace.api.config.CiVisibilityConfig.GIT_PULL_REQUEST_BASE_BRANCH; import static datadog.trace.api.config.CiVisibilityConfig.GIT_PULL_REQUEST_BASE_BRANCH_SHA; +import static datadog.trace.api.config.CiVisibilityConfig.TEST_FAILED_TEST_REPLAY_ENABLED; import static datadog.trace.api.config.CiVisibilityConfig.TEST_MANAGEMENT_ATTEMPT_TO_FIX_RETRIES; import static datadog.trace.api.config.CiVisibilityConfig.TEST_MANAGEMENT_ENABLED; import static datadog.trace.api.config.CiVisibilityConfig.TEST_SESSION_NAME; @@ -1002,6 +1003,7 @@ public static String getHostName() { private final String gitPullRequestBaseBranch; private final String gitPullRequestBaseBranchSha; private final String gitCommitHeadSha; + private final boolean ciVisibilityFailedTestReplayEnabled; private final boolean remoteConfigEnabled; private final boolean remoteConfigIntegrityCheckEnabled; @@ -2248,6 +2250,8 @@ PROFILING_DATADOG_PROFILER_ENABLED, isDatadogProfilerSafeInCurrentEnvironment()) gitPullRequestBaseBranch = configProvider.getString(GIT_PULL_REQUEST_BASE_BRANCH); gitPullRequestBaseBranchSha = configProvider.getString(GIT_PULL_REQUEST_BASE_BRANCH_SHA); gitCommitHeadSha = configProvider.getString(GIT_COMMIT_HEAD_SHA); + ciVisibilityFailedTestReplayEnabled = + configProvider.getBoolean(TEST_FAILED_TEST_REPLAY_ENABLED, true); remoteConfigEnabled = configProvider.getBoolean( @@ -3832,6 +3836,10 @@ public Integer getCiVisibilityTestManagementAttemptToFixRetries() { return ciVisibilityTestManagementAttemptToFixRetries; } + public boolean isCiVisibilityFailedTestReplayEnabled() { + return ciVisibilityFailedTestReplayEnabled; + } + public String getGitPullRequestBaseBranch() { return gitPullRequestBaseBranch; } From c4c84d6ab74db30e8429600289e7fdfb4fd91607 Mon Sep 17 00:00:00 2001 From: Daniel Mohedano Date: Mon, 14 Jul 2025 12:41:49 +0200 Subject: [PATCH 4/8] add basic exception replay integration in agent mode --- .../civisibility/CiVisibilitySystem.java | 4 +++ .../buildsystem/BuildSystemModuleImpl.java | 16 ++++++++++ .../exception/DefaultExceptionDebugger.java | 32 +++++++++++++++++-- .../com/datadog/debugger/sink/Snapshot.java | 13 ++++++++ .../main/java/datadog/trace/api/Config.java | 8 ++++- 5 files changed, 70 insertions(+), 3 deletions(-) diff --git a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/CiVisibilitySystem.java b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/CiVisibilitySystem.java index 575e9a38442..21de314fc84 100644 --- a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/CiVisibilitySystem.java +++ b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/CiVisibilitySystem.java @@ -101,6 +101,10 @@ public static void start(Instrumentation inst, SharedCommunicationObjects sco) { inst.addTransformer(new CoverageClassTransformer(instrumentationFilter)); } + if (executionSettings.isFailedTestReplayEnabled()) { + // TODO + } + CiVisibilityCoverageServices.Child coverageServices = new CiVisibilityCoverageServices.Child(services, repoServices, executionSettings); TestEventsHandlerFactory testEventsHandlerFactory = diff --git a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/domain/buildsystem/BuildSystemModuleImpl.java b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/domain/buildsystem/BuildSystemModuleImpl.java index 4fc0c27f59a..f52ec297043 100644 --- a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/domain/buildsystem/BuildSystemModuleImpl.java +++ b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/domain/buildsystem/BuildSystemModuleImpl.java @@ -12,7 +12,9 @@ import datadog.trace.api.civisibility.domain.JavaAgent; import datadog.trace.api.civisibility.telemetry.CiVisibilityMetricCollector; import datadog.trace.api.config.CiVisibilityConfig; +import datadog.trace.api.config.DebuggerConfig; import datadog.trace.api.config.GeneralConfig; +import datadog.trace.api.config.RemoteConfigConfig; import datadog.trace.bootstrap.instrumentation.api.AgentSpan; import datadog.trace.bootstrap.instrumentation.api.AgentSpanContext; import datadog.trace.bootstrap.instrumentation.api.Tags; @@ -182,6 +184,20 @@ private Map getPropertiesPropagatedToChildProcess( Strings.propertyNameToSystemPropertyName(CiVisibilityConfig.TEST_MANAGEMENT_ENABLED), Boolean.toString(executionSettings.getTestManagementSettings().isEnabled())); + // enable exception replay if failed test replay is enabled + if (executionSettings.isFailedTestReplayEnabled()) { + propagatedSystemProperties.put( + Strings.propertyNameToSystemPropertyName( + CiVisibilityConfig.TEST_FAILED_TEST_REPLAY_ENABLED), + "true"); + propagatedSystemProperties.put( + Strings.propertyNameToSystemPropertyName(DebuggerConfig.EXCEPTION_REPLAY_ENABLED), + "true"); + propagatedSystemProperties.put( + Strings.propertyNameToSystemPropertyName(RemoteConfigConfig.REMOTE_CONFIGURATION_ENABLED), + "true"); + } + // explicitly disable build instrumentation in child processes, // because some projects run "embedded" Maven/Gradle builds as part of their integration tests, // and we don't want to show those as if they were regular build executions 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..5ed6ceda72b 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; @@ -34,6 +35,10 @@ public class DefaultExceptionDebugger implements DebuggerContext.ExceptionDebugg public static final String ERROR_DEBUG_INFO_CAPTURED = "error.debug_info_captured"; public static final String SNAPSHOT_ID_TAG_FMT = DD_DEBUG_ERROR_PREFIX + "%d.snapshot_id"; + // Test Optimization / Failed Test Replay specific + public static final String TEST_DEBUG_ERROR_FILE_TAG_FMT = DD_DEBUG_ERROR_PREFIX + "%d.file"; + public static final String TEST_DEBUG_ERROR_LINE_TAG_FMT = DD_DEBUG_ERROR_PREFIX + "%d.line"; + private final ExceptionProbeManager exceptionProbeManager; private final ConfigurationUpdater configurationUpdater; private final ClassNameFilter classNameFiltering; @@ -69,7 +74,7 @@ public DefaultExceptionDebugger( @Override public void handleException(Throwable t, AgentSpan span) { - if (t instanceof Error) { + if (t instanceof Error && !Config.get().isCiVisibilityEnabled()) { if (LOGGER.isDebugEnabled()) { LOGGER.debug("Skip handling error: {}", t.toString()); } @@ -93,6 +98,7 @@ public void handleException(Throwable t, AgentSpan span) { if (exceptionProbeManager.isAlreadyInstrumented(fingerprint)) { ThrowableState state = exceptionProbeManager.getStateByThrowable(innerMostException); if (state == null) { + LOGGER.info("Unable to find state for throwable: {}", innerMostException.toString()); LOGGER.debug("Unable to find state for throwable: {}", innerMostException.toString()); return; } @@ -108,7 +114,12 @@ public void handleException(Throwable t, AgentSpan span) { exceptionProbeManager.createProbesForException( throwable.getStackTrace(), chainedExceptionIdx); if (creationResult.probesCreated > 0) { - AgentTaskScheduler.INSTANCE.execute(() -> applyExceptionConfiguration(fingerprint)); + LOGGER.info("Creating probes for: {}", t.getMessage()); + if (Config.get().isCiVisibilityEnabled()) { + applyExceptionConfiguration(fingerprint); + } else { + AgentTaskScheduler.INSTANCE.execute(() -> applyExceptionConfiguration(fingerprint)); + } break; } else { if (LOGGER.isDebugEnabled()) { @@ -147,6 +158,7 @@ private static void processSnapshotsAndSetTags( } }); } + LOGGER.info("Processing exception snapshot for: {}", t.getMessage()); boolean snapshotAssigned = false; List snapshots = state.getSnapshots(); int maxSnapshotSize = Math.min(snapshots.size(), maxCapturedFrames); @@ -166,10 +178,26 @@ private static void processSnapshotsAndSetTags( String tagName = String.format(SNAPSHOT_ID_TAG_FMT, frameIndex); span.setTag(tagName, snapshot.getId()); LOGGER.debug("add tag to span[{}]: {}: {}", span.getSpanId(), tagName, snapshot.getId()); + + StackTraceElement stackFrame = innerTrace[currentIdx]; + String fileTag = String.format(TEST_DEBUG_ERROR_FILE_TAG_FMT, frameIndex); + String lineTag = String.format(TEST_DEBUG_ERROR_LINE_TAG_FMT, frameIndex); + span.setTag(fileTag, stackFrame.getFileName()); + span.setTag(lineTag, stackFrame.getLineNumber()); + + LOGGER.debug( + "add ftr debug tags to span[{}]: {}={}, {}={}", + span.getSpanId(), + fileTag, + stackFrame.getFileName(), + lineTag, + stackFrame.getLineNumber()); + if (!state.isSnapshotSent()) { DebuggerAgent.getSink().addSnapshot(snapshot); } snapshotAssigned = true; + LOGGER.info("Capture for {}: {}", t.getMessage(), snapshots.get(i).getVariables()); } if (snapshotAssigned) { state.markAsSnapshotSent(); diff --git a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/sink/Snapshot.java b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/sink/Snapshot.java index 1de96d27c34..02877e1c424 100644 --- a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/sink/Snapshot.java +++ b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/sink/Snapshot.java @@ -12,6 +12,7 @@ import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.stream.Collectors; /** Data class representing all data collected at a probe location */ public class Snapshot { @@ -161,6 +162,18 @@ public List getEvaluationErrors() { return evaluationErrors; } + public String getVariables() { + String variables = ""; + CapturedContext returnContext = captures.getReturn(); + if (returnContext != null) { + Map allVars = new HashMap<>(); + if (returnContext.getArguments() != null) allVars.putAll(returnContext.getArguments()); + if (returnContext.getLocals() != null) allVars.putAll(returnContext.getLocals()); + variables = allVars.entrySet().stream().map(e -> e.getKey() + "=" + e.getValue().getStrValue()).collect(Collectors.joining(", ")); + } + return variables; + } + public void addEvaluationErrors(List errors) { if (errors == null || errors.isEmpty()) { return; 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 d0886af9fc2..a8572ce2c5f 100644 --- a/internal-api/src/main/java/datadog/trace/api/Config.java +++ b/internal-api/src/main/java/datadog/trace/api/Config.java @@ -4009,7 +4009,13 @@ public Set getThirdPartyShadingIdentifiers() { } private String getFinalDebuggerBaseUrl() { - if (agentUrl.startsWith("unix:")) { + if (isCiVisibilityEnabled() && isCiVisibilityAgentlessEnabled()) { + String agentlessUrl = getCiVisibilityAgentlessUrl(); + if (Strings.isNotBlank(agentlessUrl)) { + return agentlessUrl; + } + return "https://http-intake.logs." + getSite(); + } else if (agentUrl.startsWith("unix:")) { // provide placeholder agent URL, in practice we'll be tunnelling over UDS return "http://" + agentHost + ":" + agentPort; } else { From ae316705c4126e56ae039c13ce8237f06eff6a48 Mon Sep 17 00:00:00 2001 From: Daniel Mohedano Date: Tue, 22 Jul 2025 16:27:03 +0200 Subject: [PATCH 5/8] feat: headless and agentless changes --- .../java/datadog/trace/bootstrap/Agent.java | 5 +--- .../civisibility/CiVisibilitySystem.java | 2 +- .../buildsystem/BuildSystemModuleImpl.java | 3 --- .../exception/DefaultExceptionDebugger.java | 11 ++++---- .../com/datadog/debugger/sink/Snapshot.java | 13 --------- .../main/java/datadog/trace/api/Config.java | 27 ++++++++++++------- 6 files changed, 24 insertions(+), 37 deletions(-) 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 2913d27c957..9641fad503b 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 @@ -577,6 +577,7 @@ public void execute() { } maybeStartAppSec(scoClass, sco); + // start before debugger to enable Failed Test Replay correctly maybeStartCiVisibility(instrumentation, scoClass, sco); // start debugger before remote config to subscribe to it before starting to poll maybeStartDebugger(instrumentation, scoClass, sco); @@ -1153,10 +1154,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-ci-visibility/src/main/java/datadog/trace/civisibility/CiVisibilitySystem.java b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/CiVisibilitySystem.java index 21de314fc84..50004983ed9 100644 --- a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/CiVisibilitySystem.java +++ b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/CiVisibilitySystem.java @@ -102,7 +102,7 @@ public static void start(Instrumentation inst, SharedCommunicationObjects sco) { } if (executionSettings.isFailedTestReplayEnabled()) { - // TODO + config.setCiVisibilityFailedTestReplayEnabled(true); } CiVisibilityCoverageServices.Child coverageServices = diff --git a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/domain/buildsystem/BuildSystemModuleImpl.java b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/domain/buildsystem/BuildSystemModuleImpl.java index f52ec297043..f2bbf9c7565 100644 --- a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/domain/buildsystem/BuildSystemModuleImpl.java +++ b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/domain/buildsystem/BuildSystemModuleImpl.java @@ -193,9 +193,6 @@ private Map getPropertiesPropagatedToChildProcess( propagatedSystemProperties.put( Strings.propertyNameToSystemPropertyName(DebuggerConfig.EXCEPTION_REPLAY_ENABLED), "true"); - propagatedSystemProperties.put( - Strings.propertyNameToSystemPropertyName(RemoteConfigConfig.REMOTE_CONFIGURATION_ENABLED), - "true"); } // explicitly disable build instrumentation in child processes, 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 5ed6ceda72b..05ede1f2d68 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 @@ -74,7 +74,8 @@ public DefaultExceptionDebugger( @Override public void handleException(Throwable t, AgentSpan span) { - if (t instanceof Error && !Config.get().isCiVisibilityEnabled()) { + // CIVIS Failed Test Replay acts on errors + if (t instanceof Error && !Config.get().isCiVisibilityFailedTestReplayEnabled()) { if (LOGGER.isDebugEnabled()) { LOGGER.debug("Skip handling error: {}", t.toString()); } @@ -98,7 +99,6 @@ public void handleException(Throwable t, AgentSpan span) { if (exceptionProbeManager.isAlreadyInstrumented(fingerprint)) { ThrowableState state = exceptionProbeManager.getStateByThrowable(innerMostException); if (state == null) { - LOGGER.info("Unable to find state for throwable: {}", innerMostException.toString()); LOGGER.debug("Unable to find state for throwable: {}", innerMostException.toString()); return; } @@ -114,8 +114,9 @@ public void handleException(Throwable t, AgentSpan span) { exceptionProbeManager.createProbesForException( throwable.getStackTrace(), chainedExceptionIdx); if (creationResult.probesCreated > 0) { - LOGGER.info("Creating probes for: {}", t.getMessage()); - if (Config.get().isCiVisibilityEnabled()) { + if (Config.get().isCiVisibilityFailedTestReplayEnabled()) { + // Assume Exception Replay is working under Failed Test Replay logic, + // instrumentation applied sync for immediate test retries applyExceptionConfiguration(fingerprint); } else { AgentTaskScheduler.INSTANCE.execute(() -> applyExceptionConfiguration(fingerprint)); @@ -158,7 +159,6 @@ private static void processSnapshotsAndSetTags( } }); } - LOGGER.info("Processing exception snapshot for: {}", t.getMessage()); boolean snapshotAssigned = false; List snapshots = state.getSnapshots(); int maxSnapshotSize = Math.min(snapshots.size(), maxCapturedFrames); @@ -197,7 +197,6 @@ private static void processSnapshotsAndSetTags( DebuggerAgent.getSink().addSnapshot(snapshot); } snapshotAssigned = true; - LOGGER.info("Capture for {}: {}", t.getMessage(), snapshots.get(i).getVariables()); } if (snapshotAssigned) { state.markAsSnapshotSent(); diff --git a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/sink/Snapshot.java b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/sink/Snapshot.java index 02877e1c424..1de96d27c34 100644 --- a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/sink/Snapshot.java +++ b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/sink/Snapshot.java @@ -12,7 +12,6 @@ import java.util.List; import java.util.Map; import java.util.Objects; -import java.util.stream.Collectors; /** Data class representing all data collected at a probe location */ public class Snapshot { @@ -162,18 +161,6 @@ public List getEvaluationErrors() { return evaluationErrors; } - public String getVariables() { - String variables = ""; - CapturedContext returnContext = captures.getReturn(); - if (returnContext != null) { - Map allVars = new HashMap<>(); - if (returnContext.getArguments() != null) allVars.putAll(returnContext.getArguments()); - if (returnContext.getLocals() != null) allVars.putAll(returnContext.getLocals()); - variables = allVars.entrySet().stream().map(e -> e.getKey() + "=" + e.getValue().getStrValue()).collect(Collectors.joining(", ")); - } - return variables; - } - public void addEvaluationErrors(List errors) { if (errors == null || errors.isEmpty()) { return; 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 a8572ce2c5f..fdcfdf69080 100644 --- a/internal-api/src/main/java/datadog/trace/api/Config.java +++ b/internal-api/src/main/java/datadog/trace/api/Config.java @@ -1003,7 +1003,7 @@ public static String getHostName() { private final String gitPullRequestBaseBranch; private final String gitPullRequestBaseBranchSha; private final String gitCommitHeadSha; - private final boolean ciVisibilityFailedTestReplayEnabled; + private boolean ciVisibilityFailedTestReplayEnabled; private final boolean remoteConfigEnabled; private final boolean remoteConfigIntegrityCheckEnabled; @@ -3840,6 +3840,10 @@ public boolean isCiVisibilityFailedTestReplayEnabled() { return ciVisibilityFailedTestReplayEnabled; } + public void setCiVisibilityFailedTestReplayEnabled(boolean enabled) { + ciVisibilityFailedTestReplayEnabled = enabled; + } + public String getGitPullRequestBaseBranch() { return gitPullRequestBaseBranch; } @@ -3961,7 +3965,7 @@ public boolean isSymbolDatabaseCompressed() { } public boolean isDebuggerExceptionEnabled() { - return debuggerExceptionEnabled; + return debuggerExceptionEnabled || ciVisibilityFailedTestReplayEnabled; } public int getDebuggerMaxExceptionPerSecond() { @@ -4009,13 +4013,7 @@ public Set getThirdPartyShadingIdentifiers() { } private String getFinalDebuggerBaseUrl() { - if (isCiVisibilityEnabled() && isCiVisibilityAgentlessEnabled()) { - String agentlessUrl = getCiVisibilityAgentlessUrl(); - if (Strings.isNotBlank(agentlessUrl)) { - return agentlessUrl; - } - return "https://http-intake.logs." + getSite(); - } else if (agentUrl.startsWith("unix:")) { + if (agentUrl.startsWith("unix:")) { // provide placeholder agent URL, in practice we'll be tunnelling over UDS return "http://" + agentHost + ":" + agentPort; } else { @@ -4024,7 +4022,16 @@ private String getFinalDebuggerBaseUrl() { } public String getFinalDebuggerSnapshotUrl() { - return getFinalDebuggerBaseUrl() + "/debugger/v1/input"; + if (isCiVisibilityFailedTestReplayEnabled() && isCiVisibilityAgentlessEnabled()) { + // Used in Failed Test Replay agentless + String agentlessUrl = getCiVisibilityAgentlessUrl(); + if (Strings.isBlank(agentlessUrl)) { + agentlessUrl = "https://http-intake.logs." + getSite(); + } + return agentlessUrl + "/api/v2/logs"; + } else { + return getFinalDebuggerBaseUrl() + "/debugger/v1/input"; + } } public String getFinalDebuggerSymDBUrl() { From c7eeac8854253eec66266e7f48b03d278a310273 Mon Sep 17 00:00:00 2001 From: Daniel Mohedano Date: Wed, 23 Jul 2025 16:18:06 +0200 Subject: [PATCH 6/8] fix: tests --- .../domain/buildsystem/BuildSystemModuleImpl.java | 1 - .../civisibility/CiVisibilityInstrumentationTest.groovy | 7 +++++++ .../debugger/exception/DefaultExceptionDebugger.java | 6 +++--- .../trace/instrumentation/junit4/CucumberUtils.java | 1 + .../datadog/trace/instrumentation/junit4/MUnitUtils.java | 1 + .../datadog/trace/instrumentation/junit4/JUnit4Utils.java | 1 + .../trace/instrumentation/junit5/JUnitPlatformUtils.java | 4 ++++ .../datadog/trace/instrumentation/karate/KarateUtils.java | 2 ++ .../trace/instrumentation/scalatest/ScalatestUtils.java | 1 + .../datadog/trace/instrumentation/testng/TestNGUtils.java | 5 ++++- internal-api/src/main/java/datadog/trace/api/Config.java | 2 +- .../trace/api/civisibility/config/LibraryCapability.java | 1 + 12 files changed, 26 insertions(+), 6 deletions(-) diff --git a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/domain/buildsystem/BuildSystemModuleImpl.java b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/domain/buildsystem/BuildSystemModuleImpl.java index f2bbf9c7565..ef1c5935dde 100644 --- a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/domain/buildsystem/BuildSystemModuleImpl.java +++ b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/domain/buildsystem/BuildSystemModuleImpl.java @@ -14,7 +14,6 @@ import datadog.trace.api.config.CiVisibilityConfig; import datadog.trace.api.config.DebuggerConfig; import datadog.trace.api.config.GeneralConfig; -import datadog.trace.api.config.RemoteConfigConfig; import datadog.trace.bootstrap.instrumentation.api.AgentSpan; import datadog.trace.bootstrap.instrumentation.api.AgentSpanContext; import datadog.trace.bootstrap.instrumentation.api.Tags; diff --git a/dd-java-agent/agent-ci-visibility/src/testFixtures/groovy/datadog/trace/civisibility/CiVisibilityInstrumentationTest.groovy b/dd-java-agent/agent-ci-visibility/src/testFixtures/groovy/datadog/trace/civisibility/CiVisibilityInstrumentationTest.groovy index e4031ecdf30..f2a4f7989ec 100644 --- a/dd-java-agent/agent-ci-visibility/src/testFixtures/groovy/datadog/trace/civisibility/CiVisibilityInstrumentationTest.groovy +++ b/dd-java-agent/agent-ci-visibility/src/testFixtures/groovy/datadog/trace/civisibility/CiVisibilityInstrumentationTest.groovy @@ -96,6 +96,7 @@ abstract class CiVisibilityInstrumentationTest extends AgentTestRunner { injectSysConfig(CiVisibilityConfig.TEST_MANAGEMENT_ENABLED, "true") injectSysConfig(CiVisibilityConfig.TEST_MANAGEMENT_ATTEMPT_TO_FIX_RETRIES, "5") injectSysConfig(CiVisibilityConfig.CIVISIBILITY_TEST_ORDER, CIConstants.FAIL_FAST_TEST_ORDER) + injectSysConfig(CiVisibilityConfig.TEST_FAILED_TEST_REPLAY_ENABLED, "true") } private static final class Settings { @@ -112,6 +113,7 @@ abstract class CiVisibilityInstrumentationTest extends AgentTestRunner { private volatile boolean earlyFlakinessDetectionEnabled private volatile boolean impactedTestsDetectionEnabled private volatile boolean testManagementEnabled + private volatile boolean failedTestReplayEnabled } private final Settings settings = new Settings() @@ -233,6 +235,7 @@ abstract class CiVisibilityInstrumentationTest extends AgentTestRunner { settings.itrEnabled, settings.flakyRetryEnabled, settings.impactedTestsDetectionEnabled, + settings.failedTestReplayEnabled, earlyFlakinessDetectionSettings, testManagementSettings, settings.itrEnabled ? "itrCorrelationId" : null, @@ -346,6 +349,10 @@ abstract class CiVisibilityInstrumentationTest extends AgentTestRunner { settings.impactedTestsDetectionEnabled = impactedTestsDetectionEnabled } + def givenFailedTestReplayEnabled(boolean failedTestReplayEnabled) { + settings.failedTestReplayEnabled = failedTestReplayEnabled + } + def assertSpansData(String testcaseName, Map replacements = [:], List ignoredTags = []) { Predicate sessionSpan = span -> span.spanType == "test_session_end" spanFilter.waitForSpan(sessionSpan, TimeUnit.SECONDS.toMillis(20)) 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 05ede1f2d68..00e6bf60da8 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 @@ -115,9 +115,9 @@ public void handleException(Throwable t, AgentSpan span) { throwable.getStackTrace(), chainedExceptionIdx); if (creationResult.probesCreated > 0) { if (Config.get().isCiVisibilityFailedTestReplayEnabled()) { - // Assume Exception Replay is working under Failed Test Replay logic, - // instrumentation applied sync for immediate test retries - applyExceptionConfiguration(fingerprint); + // Assume Exception Replay is working under Failed Test Replay logic, + // instrumentation applied sync for immediate test retries + applyExceptionConfiguration(fingerprint); } else { AgentTaskScheduler.INSTANCE.execute(() -> applyExceptionConfiguration(fingerprint)); } diff --git a/dd-java-agent/instrumentation/junit-4.10/cucumber-junit-4/src/main/java/datadog/trace/instrumentation/junit4/CucumberUtils.java b/dd-java-agent/instrumentation/junit-4.10/cucumber-junit-4/src/main/java/datadog/trace/instrumentation/junit4/CucumberUtils.java index d3014374988..7587e58373d 100644 --- a/dd-java-agent/instrumentation/junit-4.10/cucumber-junit-4/src/main/java/datadog/trace/instrumentation/junit4/CucumberUtils.java +++ b/dd-java-agent/instrumentation/junit-4.10/cucumber-junit-4/src/main/java/datadog/trace/instrumentation/junit4/CucumberUtils.java @@ -73,6 +73,7 @@ public static String getVersion() { LibraryCapability.TIA, LibraryCapability.ATR, LibraryCapability.EFD, + LibraryCapability.FTR, LibraryCapability.QUARANTINE, LibraryCapability.DISABLED, LibraryCapability.ATTEMPT_TO_FIX); diff --git a/dd-java-agent/instrumentation/junit-4.10/munit-junit-4/src/main/java/datadog/trace/instrumentation/junit4/MUnitUtils.java b/dd-java-agent/instrumentation/junit-4.10/munit-junit-4/src/main/java/datadog/trace/instrumentation/junit4/MUnitUtils.java index cb0755ffe0d..3ba001c3ba4 100644 --- a/dd-java-agent/instrumentation/junit-4.10/munit-junit-4/src/main/java/datadog/trace/instrumentation/junit4/MUnitUtils.java +++ b/dd-java-agent/instrumentation/junit-4.10/munit-junit-4/src/main/java/datadog/trace/instrumentation/junit4/MUnitUtils.java @@ -27,6 +27,7 @@ public abstract class MUnitUtils { LibraryCapability.ATR, LibraryCapability.EFD, LibraryCapability.IMPACTED, + LibraryCapability.FTR, LibraryCapability.QUARANTINE, LibraryCapability.ATTEMPT_TO_FIX); diff --git a/dd-java-agent/instrumentation/junit-4.10/src/main/java/datadog/trace/instrumentation/junit4/JUnit4Utils.java b/dd-java-agent/instrumentation/junit-4.10/src/main/java/datadog/trace/instrumentation/junit4/JUnit4Utils.java index 2351d58a076..9b46dc9b351 100644 --- a/dd-java-agent/instrumentation/junit-4.10/src/main/java/datadog/trace/instrumentation/junit4/JUnit4Utils.java +++ b/dd-java-agent/instrumentation/junit-4.10/src/main/java/datadog/trace/instrumentation/junit4/JUnit4Utils.java @@ -65,6 +65,7 @@ public abstract class JUnit4Utils { LibraryCapability.ATR, LibraryCapability.EFD, LibraryCapability.IMPACTED, + LibraryCapability.FTR, LibraryCapability.QUARANTINE, LibraryCapability.DISABLED, LibraryCapability.ATTEMPT_TO_FIX); diff --git a/dd-java-agent/instrumentation/junit-5.3/src/main/java/datadog/trace/instrumentation/junit5/JUnitPlatformUtils.java b/dd-java-agent/instrumentation/junit-5.3/src/main/java/datadog/trace/instrumentation/junit5/JUnitPlatformUtils.java index d8435812082..d2b62499aa1 100644 --- a/dd-java-agent/instrumentation/junit-5.3/src/main/java/datadog/trace/instrumentation/junit5/JUnitPlatformUtils.java +++ b/dd-java-agent/instrumentation/junit-5.3/src/main/java/datadog/trace/instrumentation/junit5/JUnitPlatformUtils.java @@ -59,6 +59,7 @@ public abstract class JUnitPlatformUtils { LibraryCapability.ATR, LibraryCapability.EFD, LibraryCapability.IMPACTED, + LibraryCapability.FTR, LibraryCapability.QUARANTINE, LibraryCapability.DISABLED, LibraryCapability.ATTEMPT_TO_FIX); @@ -69,6 +70,7 @@ public abstract class JUnitPlatformUtils { LibraryCapability.ATR, LibraryCapability.EFD, LibraryCapability.IMPACTED, + LibraryCapability.FTR, LibraryCapability.QUARANTINE, LibraryCapability.DISABLED, LibraryCapability.ATTEMPT_TO_FIX, @@ -80,6 +82,7 @@ public abstract class JUnitPlatformUtils { LibraryCapability.ATR, LibraryCapability.EFD, LibraryCapability.IMPACTED, + LibraryCapability.FTR, LibraryCapability.QUARANTINE, LibraryCapability.DISABLED, LibraryCapability.ATTEMPT_TO_FIX); @@ -89,6 +92,7 @@ public abstract class JUnitPlatformUtils { LibraryCapability.TIA, LibraryCapability.ATR, LibraryCapability.EFD, + LibraryCapability.FTR, LibraryCapability.QUARANTINE, LibraryCapability.DISABLED, LibraryCapability.ATTEMPT_TO_FIX); diff --git a/dd-java-agent/instrumentation/karate/src/main/java/datadog/trace/instrumentation/karate/KarateUtils.java b/dd-java-agent/instrumentation/karate/src/main/java/datadog/trace/instrumentation/karate/KarateUtils.java index 3fc6348aa6e..72ac6775cbe 100644 --- a/dd-java-agent/instrumentation/karate/src/main/java/datadog/trace/instrumentation/karate/KarateUtils.java +++ b/dd-java-agent/instrumentation/karate/src/main/java/datadog/trace/instrumentation/karate/KarateUtils.java @@ -56,6 +56,7 @@ private KarateUtils() {} Arrays.asList( LibraryCapability.ATR, LibraryCapability.EFD, + LibraryCapability.FTR, LibraryCapability.QUARANTINE, LibraryCapability.ATTEMPT_TO_FIX); @@ -63,6 +64,7 @@ private KarateUtils() {} Arrays.asList( LibraryCapability.ATR, LibraryCapability.EFD, + LibraryCapability.FTR, LibraryCapability.QUARANTINE, LibraryCapability.ATTEMPT_TO_FIX, LibraryCapability.TIA, diff --git a/dd-java-agent/instrumentation/scalatest/src/main/java/datadog/trace/instrumentation/scalatest/ScalatestUtils.java b/dd-java-agent/instrumentation/scalatest/src/main/java/datadog/trace/instrumentation/scalatest/ScalatestUtils.java index 140b276244a..c6be5171199 100644 --- a/dd-java-agent/instrumentation/scalatest/src/main/java/datadog/trace/instrumentation/scalatest/ScalatestUtils.java +++ b/dd-java-agent/instrumentation/scalatest/src/main/java/datadog/trace/instrumentation/scalatest/ScalatestUtils.java @@ -24,6 +24,7 @@ public abstract class ScalatestUtils { LibraryCapability.EFD, LibraryCapability.ATR, LibraryCapability.IMPACTED, + LibraryCapability.FTR, LibraryCapability.QUARANTINE, LibraryCapability.DISABLED, LibraryCapability.ATTEMPT_TO_FIX); diff --git a/dd-java-agent/instrumentation/testng/src/main/java/datadog/trace/instrumentation/testng/TestNGUtils.java b/dd-java-agent/instrumentation/testng/src/main/java/datadog/trace/instrumentation/testng/TestNGUtils.java index b29bdae6d25..8a012d69270 100644 --- a/dd-java-agent/instrumentation/testng/src/main/java/datadog/trace/instrumentation/testng/TestNGUtils.java +++ b/dd-java-agent/instrumentation/testng/src/main/java/datadog/trace/instrumentation/testng/TestNGUtils.java @@ -282,7 +282,10 @@ public static List capabilities(String version) { List baseCapabilities = new ArrayList<>( Arrays.asList( - LibraryCapability.TIA, LibraryCapability.IMPACTED, LibraryCapability.DISABLED)); + LibraryCapability.TIA, + LibraryCapability.IMPACTED, + LibraryCapability.FTR, + LibraryCapability.DISABLED)); boolean isEFDSupported = isEFDSupported(version); boolean isExceptionSuppressionSupported = isExceptionSuppressionSupported(version); 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 fd99dcae99e..f3853485e08 100644 --- a/internal-api/src/main/java/datadog/trace/api/Config.java +++ b/internal-api/src/main/java/datadog/trace/api/Config.java @@ -2296,7 +2296,7 @@ PROFILING_DATADOG_PROFILER_ENABLED, isDatadogProfilerSafeInCurrentEnvironment()) gitPullRequestBaseBranchSha = configProvider.getString(GIT_PULL_REQUEST_BASE_BRANCH_SHA); gitCommitHeadSha = configProvider.getString(GIT_COMMIT_HEAD_SHA); ciVisibilityFailedTestReplayEnabled = - configProvider.getBoolean(TEST_FAILED_TEST_REPLAY_ENABLED, true); + configProvider.getBoolean(TEST_FAILED_TEST_REPLAY_ENABLED, false); remoteConfigEnabled = configProvider.getBoolean( diff --git a/internal-api/src/main/java/datadog/trace/api/civisibility/config/LibraryCapability.java b/internal-api/src/main/java/datadog/trace/api/civisibility/config/LibraryCapability.java index f4ed209ae30..9087e7d5783 100644 --- a/internal-api/src/main/java/datadog/trace/api/civisibility/config/LibraryCapability.java +++ b/internal-api/src/main/java/datadog/trace/api/civisibility/config/LibraryCapability.java @@ -6,6 +6,7 @@ public enum LibraryCapability { ATR("auto_test_retries", "1"), IMPACTED("impacted_tests", "1"), FAIL_FAST("fail_fast_test_order", "1"), + FTR("failed_test_replay", "1"), QUARANTINE("test_management.quarantine", "1"), DISABLED("test_management.disable", "1"), ATTEMPT_TO_FIX("test_management.attempt_to_fix", "5"); From 14cf11c43da99090b4aae1a95bc3d532a7b88d49 Mon Sep 17 00:00:00 2001 From: Daniel Mohedano Date: Mon, 4 Aug 2025 10:21:14 +0200 Subject: [PATCH 7/8] fix: testng capabilities --- .../datadog/trace/instrumentation/testng/TestNGUtils.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/dd-java-agent/instrumentation/testng/src/main/java/datadog/trace/instrumentation/testng/TestNGUtils.java b/dd-java-agent/instrumentation/testng/src/main/java/datadog/trace/instrumentation/testng/TestNGUtils.java index 8a012d69270..c67da143832 100644 --- a/dd-java-agent/instrumentation/testng/src/main/java/datadog/trace/instrumentation/testng/TestNGUtils.java +++ b/dd-java-agent/instrumentation/testng/src/main/java/datadog/trace/instrumentation/testng/TestNGUtils.java @@ -282,15 +282,13 @@ public static List capabilities(String version) { List baseCapabilities = new ArrayList<>( Arrays.asList( - LibraryCapability.TIA, - LibraryCapability.IMPACTED, - LibraryCapability.FTR, - LibraryCapability.DISABLED)); + LibraryCapability.TIA, LibraryCapability.IMPACTED, LibraryCapability.DISABLED)); boolean isEFDSupported = isEFDSupported(version); boolean isExceptionSuppressionSupported = isExceptionSuppressionSupported(version); if (isExceptionSuppressionSupported) { baseCapabilities.add(LibraryCapability.ATR); + baseCapabilities.add(LibraryCapability.FTR); baseCapabilities.add(LibraryCapability.QUARANTINE); } if (isEFDSupported) { From 461fb1f24eddb78b36b4b21cc88a33e6b8585068 Mon Sep 17 00:00:00 2001 From: Daniel Mohedano Date: Wed, 6 Aug 2025 09:42:39 +0200 Subject: [PATCH 8/8] feat: refactor agentless intakes --- .../communication/BackendApiFactory.java | 45 ++-------------- .../civisibility/CiVisibilityServices.java | 3 +- .../config/ConfigurationApiImplTest.groovy | 4 +- .../trace/logging/intake/LogsWriterImpl.java | 4 +- .../main/java/datadog/trace/api/Config.java | 14 ++--- .../trace/api/intake/AgentlessIntake.java | 52 +++++++++++++++++++ 6 files changed, 69 insertions(+), 53 deletions(-) create mode 100644 internal-api/src/main/java/datadog/trace/api/intake/AgentlessIntake.java diff --git a/communication/src/main/java/datadog/communication/BackendApiFactory.java b/communication/src/main/java/datadog/communication/BackendApiFactory.java index f3382792baa..9ff1d78aba9 100644 --- a/communication/src/main/java/datadog/communication/BackendApiFactory.java +++ b/communication/src/main/java/datadog/communication/BackendApiFactory.java @@ -5,8 +5,8 @@ import datadog.communication.http.HttpRetryPolicy; import datadog.communication.http.OkHttpUtils; import datadog.trace.api.Config; +import datadog.trace.api.intake.AgentlessIntake; import datadog.trace.util.throwable.FatalAgentMisconfigurationError; -import java.util.function.Function; import javax.annotation.Nullable; import okhttp3.HttpUrl; import okhttp3.OkHttpClient; @@ -25,11 +25,11 @@ public BackendApiFactory(Config config, SharedCommunicationObjects sharedCommuni this.sharedCommunicationObjects = sharedCommunicationObjects; } - public @Nullable BackendApi createBackendApi(Intake intake) { + public @Nullable BackendApi createBackendApi(AgentlessIntake intake) { HttpRetryPolicy.Factory retryPolicyFactory = new HttpRetryPolicy.Factory(5, 100, 2.0, true); - if (intake.agentlessModeEnabled.apply(config)) { - HttpUrl agentlessUrl = getAgentlessUrl(intake); + if (intake.isAgentlessEnabled(config)) { + HttpUrl agentlessUrl = HttpUrl.get(intake.getAgentlessUrl(config)); String apiKey = config.getApiKey(); if (apiKey == null || apiKey.isEmpty()) { throw new FatalAgentMisconfigurationError( @@ -58,41 +58,4 @@ public BackendApiFactory(Config config, SharedCommunicationObjects sharedCommuni + "and agent does not support EVP proxy"); return null; } - - private HttpUrl getAgentlessUrl(Intake intake) { - String customUrl = intake.customUrl.apply(config); - if (customUrl != null && !customUrl.isEmpty()) { - return HttpUrl.get(String.format("%s/api/%s/", customUrl, intake.version)); - } else { - String site = config.getSite(); - return HttpUrl.get( - String.format("https://%s.%s/api/%s/", intake.urlPrefix, site, intake.version)); - } - } - - public enum Intake { - API("api", "v2", Config::isCiVisibilityAgentlessEnabled, Config::getCiVisibilityAgentlessUrl), - LLMOBS_API("api", "v2", Config::isLlmObsAgentlessEnabled, Config::getLlMObsAgentlessUrl), - LOGS( - "http-intake.logs", - "v2", - Config::isAgentlessLogSubmissionEnabled, - Config::getAgentlessLogSubmissionUrl); - - public final String urlPrefix; - public final String version; - public final Function agentlessModeEnabled; - public final Function customUrl; - - Intake( - String urlPrefix, - String version, - Function agentlessModeEnabled, - Function customUrl) { - this.urlPrefix = urlPrefix; - this.version = version; - this.agentlessModeEnabled = agentlessModeEnabled; - this.customUrl = customUrl; - } - } } diff --git a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/CiVisibilityServices.java b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/CiVisibilityServices.java index f46986f31a5..29948bf87b7 100644 --- a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/CiVisibilityServices.java +++ b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/CiVisibilityServices.java @@ -34,6 +34,7 @@ import datadog.trace.civisibility.source.LinesResolver; import datadog.trace.civisibility.source.index.*; import datadog.trace.civisibility.utils.ShellCommandExecutor; +import datadog.trace.api.intake.AgentlessIntake; import java.io.File; import java.lang.reflect.Type; import java.net.InetSocketAddress; @@ -84,7 +85,7 @@ public class CiVisibilityServices { this.config = config; this.metricCollector = metricCollector; this.backendApi = - new BackendApiFactory(config, sco).createBackendApi(BackendApiFactory.Intake.API); + new BackendApiFactory(config, sco).createBackendApi(AgentlessIntake.API); this.jvmInfoFactory = new CachingJvmInfoFactory(config, new JvmInfoFactoryImpl()); this.gitClientFactory = buildGitClientFactory(config, metricCollector); diff --git a/dd-java-agent/agent-ci-visibility/src/test/groovy/datadog/trace/civisibility/config/ConfigurationApiImplTest.groovy b/dd-java-agent/agent-ci-visibility/src/test/groovy/datadog/trace/civisibility/config/ConfigurationApiImplTest.groovy index 2236988953c..92c19ca631c 100644 --- a/dd-java-agent/agent-ci-visibility/src/test/groovy/datadog/trace/civisibility/config/ConfigurationApiImplTest.groovy +++ b/dd-java-agent/agent-ci-visibility/src/test/groovy/datadog/trace/civisibility/config/ConfigurationApiImplTest.groovy @@ -1,7 +1,6 @@ package datadog.trace.civisibility.config import datadog.communication.BackendApi -import datadog.communication.BackendApiFactory import datadog.communication.EvpProxyApi import datadog.communication.IntakeApi import datadog.communication.http.HttpRetryPolicy @@ -12,6 +11,7 @@ import datadog.trace.api.civisibility.config.TestIdentifier import datadog.trace.api.civisibility.config.TestMetadata import datadog.trace.api.civisibility.telemetry.CiVisibilityMetricCollector import datadog.trace.civisibility.CiVisibilityTestUtils +import datadog.trace.api.intake.AgentlessIntake import okhttp3.HttpUrl import okhttp3.OkHttpClient import org.apache.commons.io.IOUtils @@ -273,7 +273,7 @@ class ConfigurationApiImplTest extends Specification { } private BackendApi givenIntakeApi(URI address, boolean responseCompression) { - HttpUrl intakeUrl = HttpUrl.get(String.format("%s/api/%s/", address.toString(), BackendApiFactory.Intake.API.version)) + HttpUrl intakeUrl = HttpUrl.get(String.format("%s/api/%s/", address.toString(), AgentlessIntake.API.getVersion())) String apiKey = "api-key" String traceId = "a-trace-id" diff --git a/dd-java-agent/agent-logs-intake/src/main/java/datadog/trace/logging/intake/LogsWriterImpl.java b/dd-java-agent/agent-logs-intake/src/main/java/datadog/trace/logging/intake/LogsWriterImpl.java index 44ef25c7c00..0f3d20b50c8 100644 --- a/dd-java-agent/agent-logs-intake/src/main/java/datadog/trace/logging/intake/LogsWriterImpl.java +++ b/dd-java-agent/agent-logs-intake/src/main/java/datadog/trace/logging/intake/LogsWriterImpl.java @@ -4,10 +4,10 @@ import datadog.communication.BackendApi; import datadog.communication.BackendApiFactory; -import datadog.communication.BackendApiFactory.Intake; import datadog.trace.api.Config; import datadog.trace.api.logging.intake.LogsWriter; import datadog.trace.util.AgentThreadFactory; +import datadog.trace.api.intake.AgentlessIntake; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -87,7 +87,7 @@ public void log(Map message) { } private void logPollingLoop() { - BackendApi backendApi = apiFactory.createBackendApi(Intake.LOGS); + BackendApi backendApi = apiFactory.createBackendApi(AgentlessIntake.LOGS); LogsDispatcher logsDispatcher = new LogsDispatcher(backendApi); while (!Thread.currentThread().isInterrupted()) { 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 f3853485e08..24da3c9fad5 100644 --- a/internal-api/src/main/java/datadog/trace/api/Config.java +++ b/internal-api/src/main/java/datadog/trace/api/Config.java @@ -640,6 +640,7 @@ import datadog.trace.api.iast.IastContext; import datadog.trace.api.iast.IastDetectionMode; import datadog.trace.api.iast.telemetry.Verbosity; +import datadog.trace.api.intake.AgentlessIntake; import datadog.trace.api.naming.SpanNaming; import datadog.trace.api.profiling.ProfilingEnablement; import datadog.trace.api.rum.RumInjectorConfig; @@ -4110,19 +4111,18 @@ private String getFinalDebuggerBaseUrl() { public String getFinalDebuggerSnapshotUrl() { if (isCiVisibilityFailedTestReplayEnabled() && isCiVisibilityAgentlessEnabled()) { - // Used in Failed Test Replay agentless - String agentlessUrl = getCiVisibilityAgentlessUrl(); - if (Strings.isBlank(agentlessUrl)) { - agentlessUrl = "https://http-intake.logs." + getSite(); - } - return agentlessUrl + "/api/v2/logs"; + return AgentlessIntake.LOGS.getAgentlessUrl(this) + "logs"; } else { return getFinalDebuggerBaseUrl() + "/debugger/v1/input"; } } public String getFinalDebuggerSymDBUrl() { - return getFinalDebuggerBaseUrl() + "/symdb/v1/input"; + if (isCiVisibilityFailedTestReplayEnabled() && isCiVisibilityAgentlessEnabled()) { + return AgentlessIntake.LOGS.getAgentlessUrl(this) + "logs"; + } else { + return getFinalDebuggerBaseUrl() + "/symdb/v1/input"; + } } public String getDynamicInstrumentationProbeFile() { diff --git a/internal-api/src/main/java/datadog/trace/api/intake/AgentlessIntake.java b/internal-api/src/main/java/datadog/trace/api/intake/AgentlessIntake.java new file mode 100644 index 00000000000..ce870f9339f --- /dev/null +++ b/internal-api/src/main/java/datadog/trace/api/intake/AgentlessIntake.java @@ -0,0 +1,52 @@ +package datadog.trace.api.intake; + +import datadog.trace.api.Config; +import java.util.function.Function; + +public enum AgentlessIntake { + API("api", "v2", Config::isCiVisibilityAgentlessEnabled, Config::getCiVisibilityAgentlessUrl), + LLMOBS_API("api", "v2", Config::isLlmObsAgentlessEnabled, Config::getLlMObsAgentlessUrl), + LOGS( + "http-intake.logs", + "v2", + Config::isAgentlessLogSubmissionEnabled, + Config::getAgentlessLogSubmissionUrl); + + public final String urlPrefix; + public final String version; + public final Function agentlessModeEnabled; + public final Function customUrl; + + AgentlessIntake( + String urlPrefix, + String version, + Function agentlessModeEnabled, + Function customUrl) { + this.urlPrefix = urlPrefix; + this.version = version; + this.agentlessModeEnabled = agentlessModeEnabled; + this.customUrl = customUrl; + } + + public String getUrlPrefix() { + return urlPrefix; + } + + public String getVersion() { + return version; + } + + public boolean isAgentlessEnabled(Config config) { + return agentlessModeEnabled.apply(config); + } + + public String getAgentlessUrl(Config config) { + String custom = customUrl.apply(config); + if (custom != null && !custom.isEmpty()) { + return String.format("%s/api/%s/", custom, version); + } else { + String site = config.getSite(); + return String.format("https://%s.%s/api/%s/", urlPrefix, site, version); + } + } +}