Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import datadog.trace.api.ProcessTags;
import datadog.trace.api.WellKnownTags;
import datadog.trace.util.PidHelper;
import datadog.trace.util.RandomUtils;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
Expand All @@ -31,8 +32,10 @@ public static class StoredConfig {
final String tags;
final String processTags;
final String runtimeId;
final String reportUUID;

StoredConfig(
String reportUUID,
String service,
String env,
String version,
Expand All @@ -45,6 +48,7 @@ public static class StoredConfig {
this.tags = tags;
this.processTags = processTags;
this.runtimeId = runtimeId;
this.reportUUID = reportUUID;
}

public static class Builder {
Expand All @@ -54,13 +58,15 @@ public static class Builder {
String tags;
String processTags;
String runtimeId;
String reportUUID;

public Builder(Config config) {
// get sane defaults
this.service = config.getServiceName();
this.env = config.getEnv();
this.version = config.getVersion();
this.runtimeId = config.getRuntimeId();
this.reportUUID = RandomUtils.randomUUID().toString();
}

public Builder service(String service) {
Expand Down Expand Up @@ -93,8 +99,14 @@ public Builder runtimeId(String runtimeId) {
return this;
}

// @VisibleForTesting
Builder reportUUID(String reportUUID) {
this.reportUUID = reportUUID;
return this;
}

public StoredConfig build() {
return new StoredConfig(service, env, version, tags, processTags, runtimeId);
return new StoredConfig(reportUUID, service, env, version, tags, processTags, runtimeId);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import datadog.crashtracking.parsers.HotspotCrashLogParser;

public final class CrashLogParser {
public static CrashLog fromHotspotCrashLog(String logText) {
return new HotspotCrashLogParser().parse(logText);
public static CrashLog fromHotspotCrashLog(String uuid, String logText) {
return new HotspotCrashLogParser().parse(uuid, logText);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
import static datadog.trace.api.config.CrashTrackingConfig.CRASH_TRACKING_PROXY_USERNAME;
import static datadog.trace.api.config.CrashTrackingConfig.CRASH_TRACKING_UPLOAD_TIMEOUT;
import static datadog.trace.api.config.CrashTrackingConfig.CRASH_TRACKING_UPLOAD_TIMEOUT_DEFAULT;
import static datadog.trace.api.telemetry.LogCollector.SEND_TELEMETRY;
import static datadog.trace.util.TraceUtils.normalizeServiceName;
import static datadog.trace.util.TraceUtils.normalizeTagValue;

import com.squareup.moshi.JsonWriter;
import datadog.common.container.ContainerInfo;
Expand All @@ -27,7 +30,6 @@
import java.time.Instant;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Scanner;
import java.util.concurrent.TimeUnit;
Expand Down Expand Up @@ -61,8 +63,6 @@ public final class CrashUploader {

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

private final Config config;
private final ConfigManager.StoredConfig storedConfig;
Expand Down Expand Up @@ -114,13 +114,32 @@ public CrashUploader(@Nonnull final ConfigManager.StoredConfig storedConfig) {
CRASH_TRACKING_UPLOAD_TIMEOUT, CRASH_TRACKING_UPLOAD_TIMEOUT_DEFAULT)));
}

public void upload(@Nonnull List<Path> files) throws IOException {
for (Path file : files) {
uploadToLogs(file);
uploadToTelemetry(file);
public void notifyCrashStarted(String error) {
// send a ping message to the telemetry to notify that the crash report started
try (Buffer buf = new Buffer();
JsonWriter writer = JsonWriter.of(buf)) {
writer.beginObject();
writer.name("crash_uuid").value(storedConfig.reportUUID);
writer.name("kind").value("Crash ping");
writer.name("current_schema_version").value("1.0");
writer
.name("message")
.value(
"Crashtracker crash ping: " + (error != null ? error : "crash processing started"));
writer.endObject();
handleCall(makeTelemetryRequest(makeTelemetryRequestBody(buf.readUtf8(), true)), "ping");

} catch (Throwable t) {
log.error("Failed to send crash ping", t);
}
}

public void upload(@Nonnull Path file) throws IOException {
String uuid = storedConfig.reportUUID;
uploadToLogs(file);
uploadToTelemetry(file, uuid);
}

@SuppressForbidden
boolean uploadToLogs(@Nonnull Path file) {
try {
Expand Down Expand Up @@ -236,24 +255,23 @@ private String extractErrorStackTrace(String fileContent, boolean redact) {
return "";
}

private String extractErrorStackTrace(String fileContent) {
return extractErrorStackTrace(fileContent, true);
}

boolean uploadToTelemetry(@Nonnull Path file) {
boolean uploadToTelemetry(@Nonnull Path file, String uuid) {
try {
String content = new String(Files.readAllBytes(file), Charset.defaultCharset());
handleCall(makeTelemetryRequest(content));
} catch (IOException e) {
log.error("Failed to upload crash file: {}", file, e);
CrashLog crashLog = CrashLogParser.fromHotspotCrashLog(uuid, content);
if (crashLog == null) {
log.error(SEND_TELEMETRY, "Failed to parse crash log with uuid {} ", uuid);
return false;
}
handleCall(makeTelemetryRequest(makeTelemetryRequestBody(crashLog.toJson(), false)), "crash");
} catch (Throwable t) {
log.error("Failed to upload crash file: {}", file, t);
return false;
}
return true;
}

private Call makeTelemetryRequest(@Nonnull String content) throws IOException {
final RequestBody requestBody = makeTelemetryRequestBody(content);

private Call makeTelemetryRequest(@Nonnull RequestBody requestBody) throws IOException {
final Map<String, String> headers = new HashMap<>();
// Set chunked transfer
MediaType contentType = requestBody.contentType();
Expand All @@ -273,11 +291,9 @@ private Call makeTelemetryRequest(@Nonnull String content) throws IOException {
.build());
}

private RequestBody makeTelemetryRequestBody(@Nonnull String content) throws IOException {
CrashLog crashLog = CrashLogParser.fromHotspotCrashLog(content);
if (crashLog == null) {
throw new IOException("Failed to parse crash log");
}
private RequestBody makeTelemetryRequestBody(@Nonnull String payload, boolean isPing)
throws IOException {

try (Buffer buf = new Buffer()) {
try (JsonWriter writer = JsonWriter.of(buf)) {
writer.beginObject();
Expand All @@ -291,11 +307,17 @@ private RequestBody makeTelemetryRequestBody(@Nonnull String content) throws IOE
writer.name("payload");
writer.beginArray();
writer.beginObject();
writer.name("message").value(crashLog.toJson());
writer.name("level").value("ERROR");
writer.name("tags").value("severity:crash");
writer.name("is_sensitive").value(true);
writer.name("is_crash").value(true);
writer.name("message").value(payload);
if (isPing) {
writer.name("level").value("DEBUG");
writer.name("is_sensitive").value(false);
writer.name("tags").value(tagsForPing(storedConfig.reportUUID));
} else {
writer.name("level").value("ERROR");
writer.name("tags").value("severity:crash");
writer.name("is_sensitive").value(true);
writer.name("is_crash").value(true);
}
writer.endObject();
writer.endArray();
writer.name("application");
Expand Down Expand Up @@ -327,32 +349,47 @@ private RequestBody makeTelemetryRequestBody(@Nonnull String content) throws IOE
}
}

private void handleCall(final Call call) {
private String tagsForPing(String uuid) {
final StringBuilder tags = new StringBuilder("is_crash_ping:true");
tags.append(",").append("language_name:jvm");
tags.append(",").append("service:").append(normalizeServiceName(storedConfig.service));
tags.append(",")
.append("language_version:")
.append(normalizeTagValue(SystemProperties.getOrDefault("java.version", "unknown")));
tags.append(",").append("tracer_version:").append(normalizeTagValue(VersionInfo.VERSION));
tags.append(",").append("uuid:").append(uuid);
return (tags.toString());
}

private void handleCall(final Call call, String kind) {
try (Response response = call.execute()) {
handleSuccess(call, response);
} catch (IOException e) {
handleFailure(e);
handleSuccess(call, response, kind);
} catch (Throwable t) {
handleFailure(t, kind);
}
}

private void handleSuccess(final Call call, final Response response) throws IOException {
private void handleSuccess(final Call call, final Response response, String kind)
throws IOException {
if (response.isSuccessful()) {
log.info(
"Successfully uploaded the crash files to {}, code = {} \"{}\"",
"Successfully uploaded the crash {} to {}, code = {} \"{}\"",
kind,
call.request().url(),
response.code(),
response.message());
} else {
log.error(
"Failed to upload crash files to {}, code = {} \"{}\", body = \"{}\"",
"Failed to upload crash {} to {}, code = {} \"{}\", body = \"{}\"",
kind,
call.request().url(),
response.code(),
response.message(),
response.body() != null ? response.body().string().trim() : "<null>");
}
}

private void handleFailure(final IOException exception) {
log.error("Failed to upload crash files, got exception", exception);
private void handleFailure(final Throwable exception, String kind) {
log.error("Failed to upload crash {}, got exception", kind, exception);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public final class CrashLog {
ADAPTER = moshi.adapter(CrashLog.class);
}

public final String uuid = RandomUtils.randomUUID().toString();
public final String uuid;

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

public CrashLog(
String uuid,
boolean incomplete,
String timestamp,
ErrorData error,
Metadata metadata,
OSInfo osInfo,
ProcInfo procInfo,
String dataSchemaVersion) {
this.uuid = uuid != null ? uuid : RandomUtils.randomUUID().toString();
this.incomplete = incomplete;
this.timestamp = timestamp;
this.error = error;
Expand Down Expand Up @@ -87,7 +89,7 @@ public int hashCode() {
}

public boolean equalsForTest(Object o) {
// for tests, we need to ignore UUID, OSInfo and Metadata part
// for tests, we need to ignore OSInfo and Metadata part
if (this == o) {
return true;
}
Expand All @@ -97,6 +99,7 @@ public boolean equalsForTest(Object o) {
CrashLog crashLog = (CrashLog) o;
return incomplete == crashLog.incomplete
&& version == crashLog.version
&& Objects.equals(uuid, crashLog.uuid)
&& Objects.equals(timestamp, crashLog.timestamp)
&& Objects.equals(error, crashLog.error)
&& Objects.equals(procInfo, crashLog.procInfo)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ private StackFrame parseLine(String line) {
return null;
}

public CrashLog parse(String crashLog) {
public CrashLog parse(String uuid, String crashLog) {
String signal = null;
String pid = null;
List<StackFrame> frames = new ArrayList<>();
Expand Down Expand Up @@ -213,7 +213,7 @@ public CrashLog parse(String crashLog) {
SystemProperties.get("os.name"),
SemanticVersion.of(SystemProperties.get("os.version")));
ProcInfo procInfo = pid != null ? new ProcInfo(pid) : null;
return new CrashLog(false, datetime, error, metadata, osInfo, procInfo, "1.0");
return new CrashLog(uuid, false, datetime, error, metadata, osInfo, procInfo, "1.0");
}

static String dateTimeToISO(String datetime) {
Expand Down
Loading
Loading