From 14d6ea5908ddbbec4d613b18229b77ebc5752440 Mon Sep 17 00:00:00 2001 From: Alexey Kuznetsov Date: Tue, 13 May 2025 13:19:47 -0400 Subject: [PATCH 1/5] Send initial telemetry in separate thread to reduce start-up latency. --- .../BootstrapInitializationTelemetry.java | 39 ++++++++++++------- 1 file changed, 26 insertions(+), 13 deletions(-) diff --git a/dd-java-agent/src/main/java/datadog/trace/bootstrap/BootstrapInitializationTelemetry.java b/dd-java-agent/src/main/java/datadog/trace/bootstrap/BootstrapInitializationTelemetry.java index c896983af22..3ffb931d44f 100644 --- a/dd-java-agent/src/main/java/datadog/trace/bootstrap/BootstrapInitializationTelemetry.java +++ b/dd-java-agent/src/main/java/datadog/trace/bootstrap/BootstrapInitializationTelemetry.java @@ -1,11 +1,9 @@ package datadog.trace.bootstrap; import datadog.json.JsonWriter; -import java.io.IOException; import java.io.OutputStream; import java.util.ArrayList; import java.util.List; -import java.util.concurrent.TimeUnit; /** Thread safe telemetry class used to relay information about tracer activation. */ public abstract class BootstrapInitializationTelemetry { @@ -179,7 +177,7 @@ public void finish() { } public interface JsonSender { - void send(byte[] payload) throws IOException; + void send(byte[] payload); } public static final class ForwarderJsonSender implements JsonSender { @@ -190,19 +188,34 @@ public static final class ForwarderJsonSender implements JsonSender { } @Override - public void send(byte[] payload) throws IOException { - ProcessBuilder builder = new ProcessBuilder(forwarderPath, "library_entrypoint"); + public void send(byte[] payload) { + ForwarderJsonSenderThread t = new ForwarderJsonSenderThread(forwarderPath, payload); + t.start(); + } + } - Process process = builder.start(); - try (OutputStream out = process.getOutputStream()) { - out.write(payload); - } + public static final class ForwarderJsonSenderThread extends Thread { + private final String forwarderPath; + private final byte[] payload; + + public ForwarderJsonSenderThread(String forwarderPath, byte[] payload) { + super("dd-forwarder-json-sender"); + this.forwarderPath = forwarderPath; + this.payload = payload; + } + + @Override + public void run() { + ProcessBuilder builder = new ProcessBuilder(forwarderPath, "library_entrypoint"); try { - process.waitFor(1, TimeUnit.SECONDS); - } catch (InterruptedException e) { - // just for hygiene, reset the interrupt status - Thread.currentThread().interrupt(); + Process process = builder.start(); + try (OutputStream out = process.getOutputStream()) { + out.write(payload); + } + } catch (Throwable e) { + // We don't have a log manager here, so just print. + System.err.println("Failed to send telemetry: " + e.getMessage()); } } } From bf64a9a8d0a739c89ce62846cdbe1c83393807f1 Mon Sep 17 00:00:00 2001 From: Alexey Kuznetsov Date: Tue, 13 May 2025 14:35:51 -0400 Subject: [PATCH 2/5] Added missing annotation and fixed typo. --- .../trace/bootstrap/BootstrapInitializationTelemetry.java | 2 ++ gradle/forbiddenApiFilters/main.txt | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/dd-java-agent/src/main/java/datadog/trace/bootstrap/BootstrapInitializationTelemetry.java b/dd-java-agent/src/main/java/datadog/trace/bootstrap/BootstrapInitializationTelemetry.java index 3ffb931d44f..5f3545bb3be 100644 --- a/dd-java-agent/src/main/java/datadog/trace/bootstrap/BootstrapInitializationTelemetry.java +++ b/dd-java-agent/src/main/java/datadog/trace/bootstrap/BootstrapInitializationTelemetry.java @@ -1,6 +1,7 @@ package datadog.trace.bootstrap; import datadog.json.JsonWriter; +import de.thetaphi.forbiddenapis.SuppressForbidden; import java.io.OutputStream; import java.util.ArrayList; import java.util.List; @@ -204,6 +205,7 @@ public ForwarderJsonSenderThread(String forwarderPath, byte[] payload) { this.payload = payload; } + @SuppressForbidden @Override public void run() { ProcessBuilder builder = new ProcessBuilder(forwarderPath, "library_entrypoint"); diff --git a/gradle/forbiddenApiFilters/main.txt b/gradle/forbiddenApiFilters/main.txt index 5d9946d4d0d..4993b965ed8 100644 --- a/gradle/forbiddenApiFilters/main.txt +++ b/gradle/forbiddenApiFilters/main.txt @@ -25,6 +25,6 @@ net.bytebuddy.matcher.ElementMatchers#isInterface() net.bytebuddy.matcher.ElementMatchers#isAbstract() # avoid System.out/err methods to prevent debug logging in production -@defaultMessage Avoid using System.out/err to prevent excess logging. To override, add @SuppressMethod. +@defaultMessage Avoid using System.out/err to prevent excess logging. To override, add @SuppressForbidden. java.lang.System#out java.lang.System#err From ad558a2aed6fde1440042a3b8691e7452d828036 Mon Sep 17 00:00:00 2001 From: Alexey Kuznetsov Date: Wed, 14 May 2025 17:54:38 -0400 Subject: [PATCH 3/5] Added support for emulating some work in main() function. --- .../BootstrapInitializationTelemetry.java | 1 + .../agent/InitializationTelemetryTest.groovy | 2 +- .../InitializationTelemetryCheck.java | 28 +++++++++++++++---- 3 files changed, 25 insertions(+), 6 deletions(-) diff --git a/dd-java-agent/src/main/java/datadog/trace/bootstrap/BootstrapInitializationTelemetry.java b/dd-java-agent/src/main/java/datadog/trace/bootstrap/BootstrapInitializationTelemetry.java index 5f3545bb3be..627aa05606e 100644 --- a/dd-java-agent/src/main/java/datadog/trace/bootstrap/BootstrapInitializationTelemetry.java +++ b/dd-java-agent/src/main/java/datadog/trace/bootstrap/BootstrapInitializationTelemetry.java @@ -191,6 +191,7 @@ public static final class ForwarderJsonSender implements JsonSender { @Override public void send(byte[] payload) { ForwarderJsonSenderThread t = new ForwarderJsonSenderThread(forwarderPath, payload); + t.setDaemon(true); t.start(); } } diff --git a/dd-java-agent/src/test/groovy/datadog/trace/agent/InitializationTelemetryTest.groovy b/dd-java-agent/src/test/groovy/datadog/trace/agent/InitializationTelemetryTest.groovy index d70b7d6e534..d47471d3b58 100644 --- a/dd-java-agent/src/test/groovy/datadog/trace/agent/InitializationTelemetryTest.groovy +++ b/dd-java-agent/src/test/groovy/datadog/trace/agent/InitializationTelemetryTest.groovy @@ -33,7 +33,7 @@ class InitializationTelemetryTest extends Specification { // agent initialization to fail. However, we should catch the exception allowing the application // to run normally. when: - def result = InitializationTelemetryCheck.runTestJvm(InitializationTelemetryCheck.BlockByteBuddy) + def result = InitializationTelemetryCheck.runTestJvm(InitializationTelemetryCheck.BlockByteBuddy, false, "sleep") then: result.exitCode == 0 diff --git a/dd-java-agent/src/test/java/jvmbootstraptest/InitializationTelemetryCheck.java b/dd-java-agent/src/test/java/jvmbootstraptest/InitializationTelemetryCheck.java index d7bf9db7d36..31b2f68778a 100644 --- a/dd-java-agent/src/test/java/jvmbootstraptest/InitializationTelemetryCheck.java +++ b/dd-java-agent/src/test/java/jvmbootstraptest/InitializationTelemetryCheck.java @@ -20,7 +20,13 @@ *

Checks edge cases where InitializationTelemetry is blocked by SecurityManagers */ public class InitializationTelemetryCheck { - public static void main(String[] args) {} + public static void main(String[] args) throws InterruptedException { + // Emulates the real application performing work in main(). + // That should give enough time to send initial telemetry from daemon thread. + if (args.length > 0 && args[0].equals("sleep")) { + Thread.sleep(1000); + } + } /** Blocks the loading of the agent bootstrap */ public static class BlockAgentLoading extends TestSecurityManager { @@ -71,12 +77,20 @@ protected boolean checkFileExecutePermission(FilePermission perm, Object ctx, St public static final Result runTestJvm(Class securityManagerClass) throws Exception { - return runTestJvm(securityManagerClass, false); + return runTestJvm(securityManagerClass, false, null); } public static final Result runTestJvm( Class securityManagerClass, boolean printStreams) throws Exception { + return runTestJvm(securityManagerClass, printStreams, null); + } + + public static final Result runTestJvm( + Class securityManagerClass, + boolean printStreams, + String mainArgs) + throws Exception { File jarFile = IntegrationTestUtils.createJarFileWithClasses(requiredClasses(securityManagerClass)); @@ -95,7 +109,7 @@ public static final Result runTestJvm( IntegrationTestUtils.runOnSeparateJvm( InitializationTelemetryCheck.class.getName(), InitializationTelemetryCheck.jvmArgs(securityManagerClass), - InitializationTelemetryCheck.mainArgs(), + InitializationTelemetryCheck.mainArgs(mainArgs), InitializationTelemetryCheck.envVars(forwarderFile), jarFile, printStreams); @@ -162,8 +176,12 @@ public static final String[] jvmArgs(Class securi } } - public static final String[] mainArgs() { - return new String[] {}; + public static final String[] mainArgs(String args) { + if (args == null) { + return new String[] {}; + } else { + return args.split(","); + } } public static final Map envVars(File forwarderFile) { From b52031c4153781c58e483523961532ad66ddb187 Mon Sep 17 00:00:00 2001 From: Alexey Kuznetsov Date: Mon, 19 May 2025 10:00:44 -0400 Subject: [PATCH 4/5] Minor cleanup. --- .../java/jvmbootstraptest/InitializationTelemetryCheck.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dd-java-agent/src/test/java/jvmbootstraptest/InitializationTelemetryCheck.java b/dd-java-agent/src/test/java/jvmbootstraptest/InitializationTelemetryCheck.java index 31b2f68778a..0db64149b3d 100644 --- a/dd-java-agent/src/test/java/jvmbootstraptest/InitializationTelemetryCheck.java +++ b/dd-java-agent/src/test/java/jvmbootstraptest/InitializationTelemetryCheck.java @@ -23,7 +23,7 @@ public class InitializationTelemetryCheck { public static void main(String[] args) throws InterruptedException { // Emulates the real application performing work in main(). // That should give enough time to send initial telemetry from daemon thread. - if (args.length > 0 && args[0].equals("sleep")) { + if (args.length > 0 && "sleep".equals(args[0])) { Thread.sleep(1000); } } From 74f62364cce674b673c0b6608f3d44ee3ca1e295 Mon Sep 17 00:00:00 2001 From: Alexey Kuznetsov Date: Tue, 20 May 2025 10:23:57 -0400 Subject: [PATCH 5/5] Fixed test. --- .../datadog/trace/agent/InitializationTelemetryTest.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dd-java-agent/src/test/groovy/datadog/trace/agent/InitializationTelemetryTest.groovy b/dd-java-agent/src/test/groovy/datadog/trace/agent/InitializationTelemetryTest.groovy index d47471d3b58..af05b91571b 100644 --- a/dd-java-agent/src/test/groovy/datadog/trace/agent/InitializationTelemetryTest.groovy +++ b/dd-java-agent/src/test/groovy/datadog/trace/agent/InitializationTelemetryTest.groovy @@ -21,7 +21,7 @@ class InitializationTelemetryTest extends Specification { def "normal start-up"() { when: - def result = InitializationTelemetryCheck.runTestJvm(null) + def result = InitializationTelemetryCheck.runTestJvm(null, false, "sleep") then: result.exitCode == 0