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..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 @@ -1,11 +1,10 @@ package datadog.trace.bootstrap; import datadog.json.JsonWriter; -import java.io.IOException; +import de.thetaphi.forbiddenapis.SuppressForbidden; 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 +178,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 +189,36 @@ 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.setDaemon(true); + 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; + } + + @SuppressForbidden + @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()); } } } 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..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 @@ -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..0db64149b3d 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 && "sleep".equals(args[0])) { + 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) { 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