Skip to content

Commit ee899e9

Browse files
committed
Crashtracking: send ping message
1 parent 92a857d commit ee899e9

File tree

11 files changed

+160
-84
lines changed

11 files changed

+160
-84
lines changed

dd-java-agent/agent-crashtracking/src/main/java/datadog/crashtracking/ConfigManager.java

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import datadog.trace.api.ProcessTags;
1010
import datadog.trace.api.WellKnownTags;
1111
import datadog.trace.util.PidHelper;
12+
import datadog.trace.util.RandomUtils;
1213
import java.io.BufferedReader;
1314
import java.io.BufferedWriter;
1415
import java.io.IOException;
@@ -31,8 +32,10 @@ public static class StoredConfig {
3132
final String tags;
3233
final String processTags;
3334
final String runtimeId;
35+
final String reportUUID;
3436

3537
StoredConfig(
38+
String reportUUID,
3639
String service,
3740
String env,
3841
String version,
@@ -45,6 +48,7 @@ public static class StoredConfig {
4548
this.tags = tags;
4649
this.processTags = processTags;
4750
this.runtimeId = runtimeId;
51+
this.reportUUID = reportUUID;
4852
}
4953

5054
public static class Builder {
@@ -54,13 +58,15 @@ public static class Builder {
5458
String tags;
5559
String processTags;
5660
String runtimeId;
61+
String reportUUID;
5762

5863
public Builder(Config config) {
5964
// get sane defaults
6065
this.service = config.getServiceName();
6166
this.env = config.getEnv();
6267
this.version = config.getVersion();
6368
this.runtimeId = config.getRuntimeId();
69+
this.reportUUID = RandomUtils.randomUUID().toString();
6470
}
6571

6672
public Builder service(String service) {
@@ -93,8 +99,14 @@ public Builder runtimeId(String runtimeId) {
9399
return this;
94100
}
95101

102+
// @VisibleForTesting
103+
Builder reportUUID(String reportUUID) {
104+
this.reportUUID = reportUUID;
105+
return this;
106+
}
107+
96108
public StoredConfig build() {
97-
return new StoredConfig(service, env, version, tags, processTags, runtimeId);
109+
return new StoredConfig(reportUUID, service, env, version, tags, processTags, runtimeId);
98110
}
99111
}
100112
}

dd-java-agent/agent-crashtracking/src/main/java/datadog/crashtracking/CrashLogParser.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import datadog.crashtracking.parsers.HotspotCrashLogParser;
55

66
public final class CrashLogParser {
7-
public static CrashLog fromHotspotCrashLog(String logText) {
8-
return new HotspotCrashLogParser().parse(logText);
7+
public static CrashLog fromHotspotCrashLog(String uuid, String logText) {
8+
return new HotspotCrashLogParser().parse(uuid, logText);
99
}
1010
}

dd-java-agent/agent-crashtracking/src/main/java/datadog/crashtracking/CrashUploader.java

Lines changed: 61 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,6 @@ public final class CrashUploader {
6161

6262
private static final MediaType APPLICATION_JSON =
6363
MediaType.get("application/json; charset=utf-8");
64-
private static final MediaType APPLICATION_OCTET_STREAM =
65-
MediaType.parse("application/octet-stream");
6664

6765
private final Config config;
6866
private final ConfigManager.StoredConfig storedConfig;
@@ -114,10 +112,34 @@ public CrashUploader(@Nonnull final ConfigManager.StoredConfig storedConfig) {
114112
CRASH_TRACKING_UPLOAD_TIMEOUT, CRASH_TRACKING_UPLOAD_TIMEOUT_DEFAULT)));
115113
}
116114

115+
public void notifyCrashStarted(String error) {
116+
// send a ping message to the telemetry to notify that the crash report started
117+
try (Buffer buf = new Buffer();
118+
JsonWriter writer = JsonWriter.of(buf)) {
119+
writer.beginObject();
120+
writer.name("crash_uuid").value(storedConfig.reportUUID);
121+
writer.name("kind").value("Crash ping");
122+
writer.name("current_schema_version").value("1.0");
123+
writer
124+
.name("message")
125+
.value(
126+
"Crashtracker crash ping: " + (error != null ? error : "crash processing started"));
127+
writer.endObject();
128+
handleCall(makeTelemetryRequest(makeTelemetryRequestBody(buf.readUtf8(), true)), "ping");
129+
130+
} catch (Throwable t) {
131+
log.error("Failed to send crash ping", t);
132+
}
133+
}
134+
117135
public void upload(@Nonnull List<Path> files) throws IOException {
136+
String uuid = storedConfig.reportUUID;
118137
for (Path file : files) {
119138
uploadToLogs(file);
120-
uploadToTelemetry(file);
139+
uploadToTelemetry(file, uuid);
140+
// if we send more than 1 file via the CLI, let's make sure we have unique uuid (will be
141+
// generated if null)
142+
uuid = null;
121143
}
122144
}
123145

@@ -236,24 +258,23 @@ private String extractErrorStackTrace(String fileContent, boolean redact) {
236258
return "";
237259
}
238260

239-
private String extractErrorStackTrace(String fileContent) {
240-
return extractErrorStackTrace(fileContent, true);
241-
}
242-
243-
boolean uploadToTelemetry(@Nonnull Path file) {
261+
boolean uploadToTelemetry(@Nonnull Path file, String uuid) {
244262
try {
245263
String content = new String(Files.readAllBytes(file), Charset.defaultCharset());
246-
handleCall(makeTelemetryRequest(content));
247-
} catch (IOException e) {
248-
log.error("Failed to upload crash file: {}", file, e);
264+
CrashLog crashLog = CrashLogParser.fromHotspotCrashLog(uuid, content);
265+
if (crashLog == null) {
266+
log.error("Failed to parse crash log");
267+
return false;
268+
}
269+
handleCall(makeTelemetryRequest(makeTelemetryRequestBody(crashLog.toJson(), false)), "crash");
270+
} catch (Throwable t) {
271+
log.error("Failed to upload crash file: {}", file, t);
249272
return false;
250273
}
251274
return true;
252275
}
253276

254-
private Call makeTelemetryRequest(@Nonnull String content) throws IOException {
255-
final RequestBody requestBody = makeTelemetryRequestBody(content);
256-
277+
private Call makeTelemetryRequest(@Nonnull RequestBody requestBody) throws IOException {
257278
final Map<String, String> headers = new HashMap<>();
258279
// Set chunked transfer
259280
MediaType contentType = requestBody.contentType();
@@ -273,11 +294,9 @@ private Call makeTelemetryRequest(@Nonnull String content) throws IOException {
273294
.build());
274295
}
275296

276-
private RequestBody makeTelemetryRequestBody(@Nonnull String content) throws IOException {
277-
CrashLog crashLog = CrashLogParser.fromHotspotCrashLog(content);
278-
if (crashLog == null) {
279-
throw new IOException("Failed to parse crash log");
280-
}
297+
private RequestBody makeTelemetryRequestBody(@Nonnull String payload, boolean isPing)
298+
throws IOException {
299+
281300
try (Buffer buf = new Buffer()) {
282301
try (JsonWriter writer = JsonWriter.of(buf)) {
283302
writer.beginObject();
@@ -291,11 +310,17 @@ private RequestBody makeTelemetryRequestBody(@Nonnull String content) throws IOE
291310
writer.name("payload");
292311
writer.beginArray();
293312
writer.beginObject();
294-
writer.name("message").value(crashLog.toJson());
295-
writer.name("level").value("ERROR");
296-
writer.name("tags").value("severity:crash");
297-
writer.name("is_sensitive").value(true);
298-
writer.name("is_crash").value(true);
313+
writer.name("message").value(payload);
314+
if (isPing) {
315+
writer.name("level").value("DEBUG");
316+
writer.name("is_sensitive").value(false);
317+
writer.name("is_crash_ping").value(true);
318+
} else {
319+
writer.name("level").value("ERROR");
320+
writer.name("tags").value("severity:crash");
321+
writer.name("is_sensitive").value(true);
322+
writer.name("is_crash").value(true);
323+
}
299324
writer.endObject();
300325
writer.endArray();
301326
writer.name("application");
@@ -327,32 +352,35 @@ private RequestBody makeTelemetryRequestBody(@Nonnull String content) throws IOE
327352
}
328353
}
329354

330-
private void handleCall(final Call call) {
355+
private void handleCall(final Call call, String kind) {
331356
try (Response response = call.execute()) {
332-
handleSuccess(call, response);
333-
} catch (IOException e) {
334-
handleFailure(e);
357+
handleSuccess(call, response, kind);
358+
} catch (Throwable t) {
359+
handleFailure(t, kind);
335360
}
336361
}
337362

338-
private void handleSuccess(final Call call, final Response response) throws IOException {
363+
private void handleSuccess(final Call call, final Response response, String kind)
364+
throws IOException {
339365
if (response.isSuccessful()) {
340366
log.info(
341-
"Successfully uploaded the crash files to {}, code = {} \"{}\"",
367+
"Successfully uploaded the crash {} to {}, code = {} \"{}\"",
368+
kind,
342369
call.request().url(),
343370
response.code(),
344371
response.message());
345372
} else {
346373
log.error(
347-
"Failed to upload crash files to {}, code = {} \"{}\", body = \"{}\"",
374+
"Failed to upload crash {} to {}, code = {} \"{}\", body = \"{}\"",
375+
kind,
348376
call.request().url(),
349377
response.code(),
350378
response.message(),
351379
response.body() != null ? response.body().string().trim() : "<null>");
352380
}
353381
}
354382

355-
private void handleFailure(final IOException exception) {
356-
log.error("Failed to upload crash files, got exception", exception);
383+
private void handleFailure(final Throwable exception, String kind) {
384+
log.error("Failed to upload crash {}, got exception", kind, exception);
357385
}
358386
}

dd-java-agent/agent-crashtracking/src/main/java/datadog/crashtracking/dto/CrashLog.java

Lines changed: 3 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ public final class CrashLog {
1717
ADAPTER = moshi.adapter(CrashLog.class);
1818
}
1919

20-
public final String uuid = RandomUtils.randomUUID().toString();
20+
public final String uuid;
2121

2222
@Json(name = "data_schema_version")
2323
public final String dataSchemaVersion;
@@ -37,13 +37,15 @@ public final class CrashLog {
3737
public final int version = VERSION;
3838

3939
public CrashLog(
40+
String uuid,
4041
boolean incomplete,
4142
String timestamp,
4243
ErrorData error,
4344
Metadata metadata,
4445
OSInfo osInfo,
4546
ProcInfo procInfo,
4647
String dataSchemaVersion) {
48+
this.uuid = uuid != null ? uuid : RandomUtils.randomUUID().toString();
4749
this.incomplete = incomplete;
4850
this.timestamp = timestamp;
4951
this.error = error;
@@ -85,21 +87,4 @@ public int hashCode() {
8587
return Objects.hash(
8688
uuid, timestamp, incomplete, error, metadata, osInfo, procInfo, version, dataSchemaVersion);
8789
}
88-
89-
public boolean equalsForTest(Object o) {
90-
// for tests, we need to ignore UUID, OSInfo and Metadata part
91-
if (this == o) {
92-
return true;
93-
}
94-
if (o == null || getClass() != o.getClass()) {
95-
return false;
96-
}
97-
CrashLog crashLog = (CrashLog) o;
98-
return incomplete == crashLog.incomplete
99-
&& version == crashLog.version
100-
&& Objects.equals(timestamp, crashLog.timestamp)
101-
&& Objects.equals(error, crashLog.error)
102-
&& Objects.equals(procInfo, crashLog.procInfo)
103-
&& Objects.equals(dataSchemaVersion, crashLog.dataSchemaVersion);
104-
}
10590
}

dd-java-agent/agent-crashtracking/src/main/java/datadog/crashtracking/parsers/HotspotCrashLogParser.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ private StackFrame parseLine(String line) {
115115
return null;
116116
}
117117

118-
public CrashLog parse(String crashLog) {
118+
public CrashLog parse(String uuid, String crashLog) {
119119
String signal = null;
120120
String pid = null;
121121
List<StackFrame> frames = new ArrayList<>();
@@ -213,7 +213,7 @@ public CrashLog parse(String crashLog) {
213213
SystemProperties.get("os.name"),
214214
SemanticVersion.of(SystemProperties.get("os.version")));
215215
ProcInfo procInfo = pid != null ? new ProcInfo(pid) : null;
216-
return new CrashLog(false, datetime, error, metadata, osInfo, procInfo, "1.0");
216+
return new CrashLog(uuid, false, datetime, error, metadata, osInfo, procInfo, "1.0");
217217
}
218218

219219
static String dateTimeToISO(String datetime) {

0 commit comments

Comments
 (0)