Skip to content

Commit 887ea39

Browse files
Surface potential root cause when agent initialization errors (#9170)
1 parent c73f8b6 commit 887ea39

File tree

2 files changed

+67
-10
lines changed

2 files changed

+67
-10
lines changed

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

Lines changed: 52 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,20 @@
11
package datadog.trace.bootstrap;
22

33
import datadog.json.JsonWriter;
4+
import datadog.trace.bootstrap.environment.EnvironmentVariables;
45
import de.thetaphi.forbiddenapis.SuppressForbidden;
56
import java.io.Closeable;
67
import java.io.OutputStream;
78
import java.util.ArrayList;
9+
import java.util.Collections;
10+
import java.util.LinkedHashMap;
811
import java.util.List;
12+
import java.util.Map;
913

1014
/** Thread safe telemetry class used to relay information about tracer activation. */
1115
public abstract class BootstrapInitializationTelemetry {
16+
private static final int DEFAULT_MAX_TAGS = 5;
17+
1218
/** Returns a singleton no op instance of initialization telemetry */
1319
public static BootstrapInitializationTelemetry noOpInstance() {
1420
return NoOp.INSTANCE;
@@ -88,7 +94,7 @@ public static final class JsonBased extends BootstrapInitializationTelemetry {
8894
private final JsonSender sender;
8995

9096
private final List<String> meta;
91-
private final List<String> points;
97+
private final Map<String, List<String>> points;
9298

9399
// one way false to true
94100
private volatile boolean incomplete = false;
@@ -97,7 +103,7 @@ public static final class JsonBased extends BootstrapInitializationTelemetry {
97103
JsonBased(JsonSender sender) {
98104
this.sender = sender;
99105
this.meta = new ArrayList<>();
100-
this.points = new ArrayList<>();
106+
this.points = new LinkedHashMap<>();
101107
}
102108

103109
@Override
@@ -118,8 +124,39 @@ public void onAbort(String reasonCode) {
118124
@Override
119125
public void onError(Throwable t) {
120126
error = true;
121-
onPoint("library_entrypoint.error", "error_type:" + t.getClass().getName());
122127
setMetaInfo("error", "internal_error", t.getMessage());
128+
129+
List<String> causes = new ArrayList<>();
130+
131+
Throwable cause = t.getCause();
132+
while (cause != null) {
133+
causes.add("error_type:" + cause.getClass().getName());
134+
cause = cause.getCause();
135+
}
136+
causes.add("error_type:" + t.getClass().getName());
137+
138+
// Limit the number of tags to avoid overpopulating the JSON payload.
139+
int maxTags = maxTags();
140+
int numCauses = causes.size();
141+
if (numCauses > maxTags) {
142+
causes = causes.subList(numCauses - maxTags, numCauses);
143+
}
144+
145+
onPoint("library_entrypoint.error", causes);
146+
}
147+
148+
private int maxTags() {
149+
String maxTags = EnvironmentVariables.get("DD_TELEMETRY_FORWARDER_MAX_TAGS");
150+
151+
if (maxTags != null) {
152+
try {
153+
return Integer.parseInt(maxTags);
154+
} catch (Throwable ignore) {
155+
// Ignore and return default value.
156+
}
157+
}
158+
159+
return DEFAULT_MAX_TAGS;
123160
}
124161

125162
@Override
@@ -159,9 +196,12 @@ private String mapResultClass(String reasonCode) {
159196
}
160197

161198
private void onPoint(String name, String tag) {
199+
onPoint(name, Collections.singletonList(tag));
200+
}
201+
202+
private void onPoint(String name, List<String> tags) {
162203
synchronized (this.points) {
163-
this.points.add(name);
164-
this.points.add(tag);
204+
this.points.put(name, tags);
165205
}
166206
}
167207

@@ -189,10 +229,14 @@ public void finish() {
189229

190230
writer.name("points").beginArray();
191231
synchronized (this.points) {
192-
for (int i = 0; i + 1 < this.points.size(); i = i + 2) {
232+
for (Map.Entry<String, List<String>> entry : points.entrySet()) {
193233
writer.beginObject();
194-
writer.name("name").value(this.points.get(i));
195-
writer.name("tags").beginArray().value(this.points.get(i + 1)).endArray();
234+
writer.name("name").value(entry.getKey());
235+
writer.name("tags").beginArray();
236+
for (String tag : entry.getValue()) {
237+
writer.value(tag);
238+
}
239+
writer.endArray();
196240
writer.endObject();
197241
}
198242
this.points.clear();

dd-java-agent/src/test/groovy/datadog/trace/bootstrap/BootstrapInitializationTelemetryTest.groovy

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package datadog.trace.bootstrap
22

3+
import groovy.json.JsonBuilder
34
import spock.lang.Specification
45

56
import static java.nio.charset.StandardCharsets.UTF_8
@@ -106,8 +107,20 @@ class BootstrapInitializationTelemetryTest extends Specification {
106107
!capture.json().contains("library_entrypoint.complete")
107108
}
108109

109-
private String json(String result, String resultClass, String resultReason, List points) {
110-
return """{"metadata":{"runtime_name":"java","runtime_version":"1.8.0_382","result":"${result}","result_class":"${resultClass}","result_reason":"${resultReason}"},"points":${new groovy.json.JsonBuilder(points)}}"""
110+
def "unwind root cause"() {
111+
when:
112+
initTelemetry.onError(new Exception("top cause", new FileNotFoundException("root cause")))
113+
initTelemetry.finish()
114+
115+
then:
116+
capture.json() == json("error", "internal_error", "top cause", [
117+
[name: "library_entrypoint.error", tags: ["error_type:java.io.FileNotFoundException", "error_type:java.lang.Exception"]],
118+
[name: "library_entrypoint.complete"]
119+
])
120+
}
121+
122+
private static String json(String result, String resultClass, String resultReason, List points) {
123+
return """{"metadata":{"runtime_name":"java","runtime_version":"1.8.0_382","result":"${result}","result_class":"${resultClass}","result_reason":"${resultReason}"},"points":${new JsonBuilder(points)}}"""
111124
}
112125

113126
static class Capture implements BootstrapInitializationTelemetry.JsonSender {

0 commit comments

Comments
 (0)