Skip to content

Commit 76393f7

Browse files
Send initial telemetry in separate thread to reduce start-up latency. (#8818)
1 parent 0ca8f80 commit 76393f7

File tree

4 files changed

+55
-21
lines changed

4 files changed

+55
-21
lines changed

dd-java-agent/src/main/java/datadog/trace/bootstrap/BootstrapInitializationTelemetry.java

Lines changed: 29 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
package datadog.trace.bootstrap;
22

33
import datadog.json.JsonWriter;
4-
import java.io.IOException;
4+
import de.thetaphi.forbiddenapis.SuppressForbidden;
55
import java.io.OutputStream;
66
import java.util.ArrayList;
77
import java.util.List;
8-
import java.util.concurrent.TimeUnit;
98

109
/** Thread safe telemetry class used to relay information about tracer activation. */
1110
public abstract class BootstrapInitializationTelemetry {
@@ -179,7 +178,7 @@ public void finish() {
179178
}
180179

181180
public interface JsonSender {
182-
void send(byte[] payload) throws IOException;
181+
void send(byte[] payload);
183182
}
184183

185184
public static final class ForwarderJsonSender implements JsonSender {
@@ -190,19 +189,36 @@ public static final class ForwarderJsonSender implements JsonSender {
190189
}
191190

192191
@Override
193-
public void send(byte[] payload) throws IOException {
194-
ProcessBuilder builder = new ProcessBuilder(forwarderPath, "library_entrypoint");
192+
public void send(byte[] payload) {
193+
ForwarderJsonSenderThread t = new ForwarderJsonSenderThread(forwarderPath, payload);
194+
t.setDaemon(true);
195+
t.start();
196+
}
197+
}
195198

196-
Process process = builder.start();
197-
try (OutputStream out = process.getOutputStream()) {
198-
out.write(payload);
199-
}
199+
public static final class ForwarderJsonSenderThread extends Thread {
200+
private final String forwarderPath;
201+
private final byte[] payload;
202+
203+
public ForwarderJsonSenderThread(String forwarderPath, byte[] payload) {
204+
super("dd-forwarder-json-sender");
205+
this.forwarderPath = forwarderPath;
206+
this.payload = payload;
207+
}
208+
209+
@SuppressForbidden
210+
@Override
211+
public void run() {
212+
ProcessBuilder builder = new ProcessBuilder(forwarderPath, "library_entrypoint");
200213

201214
try {
202-
process.waitFor(1, TimeUnit.SECONDS);
203-
} catch (InterruptedException e) {
204-
// just for hygiene, reset the interrupt status
205-
Thread.currentThread().interrupt();
215+
Process process = builder.start();
216+
try (OutputStream out = process.getOutputStream()) {
217+
out.write(payload);
218+
}
219+
} catch (Throwable e) {
220+
// We don't have a log manager here, so just print.
221+
System.err.println("Failed to send telemetry: " + e.getMessage());
206222
}
207223
}
208224
}

dd-java-agent/src/test/groovy/datadog/trace/agent/InitializationTelemetryTest.groovy

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ class InitializationTelemetryTest extends Specification {
2121

2222
def "normal start-up"() {
2323
when:
24-
def result = InitializationTelemetryCheck.runTestJvm(null)
24+
def result = InitializationTelemetryCheck.runTestJvm(null, false, "sleep")
2525

2626
then:
2727
result.exitCode == 0
@@ -33,7 +33,7 @@ class InitializationTelemetryTest extends Specification {
3333
// agent initialization to fail. However, we should catch the exception allowing the application
3434
// to run normally.
3535
when:
36-
def result = InitializationTelemetryCheck.runTestJvm(InitializationTelemetryCheck.BlockByteBuddy)
36+
def result = InitializationTelemetryCheck.runTestJvm(InitializationTelemetryCheck.BlockByteBuddy, false, "sleep")
3737

3838
then:
3939
result.exitCode == 0

dd-java-agent/src/test/java/jvmbootstraptest/InitializationTelemetryCheck.java

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,13 @@
2020
* <p>Checks edge cases where InitializationTelemetry is blocked by SecurityManagers
2121
*/
2222
public class InitializationTelemetryCheck {
23-
public static void main(String[] args) {}
23+
public static void main(String[] args) throws InterruptedException {
24+
// Emulates the real application performing work in main().
25+
// That should give enough time to send initial telemetry from daemon thread.
26+
if (args.length > 0 && "sleep".equals(args[0])) {
27+
Thread.sleep(1000);
28+
}
29+
}
2430

2531
/** Blocks the loading of the agent bootstrap */
2632
public static class BlockAgentLoading extends TestSecurityManager {
@@ -71,12 +77,20 @@ protected boolean checkFileExecutePermission(FilePermission perm, Object ctx, St
7177

7278
public static final Result runTestJvm(Class<? extends TestSecurityManager> securityManagerClass)
7379
throws Exception {
74-
return runTestJvm(securityManagerClass, false);
80+
return runTestJvm(securityManagerClass, false, null);
7581
}
7682

7783
public static final Result runTestJvm(
7884
Class<? extends TestSecurityManager> securityManagerClass, boolean printStreams)
7985
throws Exception {
86+
return runTestJvm(securityManagerClass, printStreams, null);
87+
}
88+
89+
public static final Result runTestJvm(
90+
Class<? extends TestSecurityManager> securityManagerClass,
91+
boolean printStreams,
92+
String mainArgs)
93+
throws Exception {
8094

8195
File jarFile =
8296
IntegrationTestUtils.createJarFileWithClasses(requiredClasses(securityManagerClass));
@@ -95,7 +109,7 @@ public static final Result runTestJvm(
95109
IntegrationTestUtils.runOnSeparateJvm(
96110
InitializationTelemetryCheck.class.getName(),
97111
InitializationTelemetryCheck.jvmArgs(securityManagerClass),
98-
InitializationTelemetryCheck.mainArgs(),
112+
InitializationTelemetryCheck.mainArgs(mainArgs),
99113
InitializationTelemetryCheck.envVars(forwarderFile),
100114
jarFile,
101115
printStreams);
@@ -162,8 +176,12 @@ public static final String[] jvmArgs(Class<? extends TestSecurityManager> securi
162176
}
163177
}
164178

165-
public static final String[] mainArgs() {
166-
return new String[] {};
179+
public static final String[] mainArgs(String args) {
180+
if (args == null) {
181+
return new String[] {};
182+
} else {
183+
return args.split(",");
184+
}
167185
}
168186

169187
public static final Map<String, String> envVars(File forwarderFile) {

gradle/forbiddenApiFilters/main.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,6 @@ net.bytebuddy.matcher.ElementMatchers#isInterface()
2525
net.bytebuddy.matcher.ElementMatchers#isAbstract()
2626

2727
# avoid System.out/err methods to prevent debug logging in production
28-
@defaultMessage Avoid using System.out/err to prevent excess logging. To override, add @SuppressMethod.
28+
@defaultMessage Avoid using System.out/err to prevent excess logging. To override, add @SuppressForbidden.
2929
java.lang.System#out
3030
java.lang.System#err

0 commit comments

Comments
 (0)