Skip to content
Merged
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
69 changes: 47 additions & 22 deletions src/main/java/com/squareup/squash/SquashBacktrace.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,35 +24,27 @@
/** Creates the Squash stacktrace format for serialization by gson. */
public final class SquashBacktrace {

// This lets Squash know that the stacktrace is java-style and can contain obfuscated classes.
public static final String JAVA_PREFIX = "_JAVA_";

private SquashBacktrace() {
// Should not be instantiated: this is a utility class.
}

public static List<List<Object>> getBacktraces(Throwable error) {
public static List<SquashException> getBacktraces(Throwable error) {
if (error == null) {
return null;
}
final ArrayList<List<Object>> threadList = new ArrayList<List<Object>>();
final ArrayList<Object> currentThread = new ArrayList<Object>();
currentThread.add(Thread.currentThread().getName());
currentThread.add(true);
currentThread.add(getStacktraceArray(error));
final List<SquashException> threadList = new ArrayList<SquashException>();
final SquashException currentThread =
new SquashException(Thread.currentThread().getName(), true, getStacktraceArray(error));
threadList.add(currentThread);
return threadList;
}

private static List<List<Object>> getStacktraceArray(Throwable error) {
List<List<Object>> stackElems = new ArrayList<List<Object>>();
private static List<StackElement> getStacktraceArray(Throwable error) {
List<StackElement> stackElems = new ArrayList<StackElement>();
for (StackTraceElement element : error.getStackTrace()) {
List<Object> elementList = new ArrayList<Object>();
elementList.add(JAVA_PREFIX);
elementList.add(element.getFileName());
elementList.add(element.getLineNumber());
elementList.add(element.getMethodName());
elementList.add(element.getClassName());
StackElement elementList =
new StackElement(element.getClassName(), element.getFileName(), element.getLineNumber(),
element.getMethodName());
stackElems.add(elementList);
}
return stackElems;
Expand Down Expand Up @@ -92,26 +84,59 @@ public static void populateNestedExceptions(List<NestedException> nestedExceptio
return;
}
final Throwable cause = error.getCause();
NestedException doc = new NestedException(cause.getClass().getName(), cause.getMessage(),
getBacktraces(cause), getIvars(cause));
NestedException doc =
new NestedException(cause.getClass().getName(), cause.getMessage(), getBacktraces(cause),
getIvars(cause));
nestedExceptions.add(doc);
// Exceptions all the way down!
populateNestedExceptions(nestedExceptions, cause);
}

/** Wrapper object for top-level exceptions. */
static final class SquashException {
final String name;
final boolean faulted;
final List<StackElement> backtrace;

public SquashException(String name, boolean faulted, List<StackElement> backtrace) {
this.backtrace = backtrace;
this.name = name;
this.faulted = faulted;
}
}

/** Wrapper object for nested exceptions. */
public static class NestedException {
static final class NestedException {
final String class_name;
final String message;
final List<List<Object>> backtraces;
final List<SquashException> backtraces;
final Map<String, Object> ivars;

public NestedException(String className, String message, List<List<Object>> backtraces,
public NestedException(String className, String message, List<SquashException> backtraces,
Map<String, Object> ivars) {
this.class_name = className;
this.message = message;
this.backtraces = backtraces;
this.ivars = ivars;
}
}

/** Wrapper object for a stacktrace entry. */
static final class StackElement {
// This field is necessary so Squash knows that this is a java stacktrace that might need
// obfuscation lookup and git filename lookup. Our stacktrace elements don't give us the full
// path to the java file, so Squash has to do a SCM lookup to try and do its cause analysis.
@SuppressWarnings("UnusedDeclaration") final String type = "obfuscated";
final String file;
final int line;
final String symbol;
final String class_name;

private StackElement(String className, String file, int line, String methodName) {
this.class_name = className;
this.file = file;
this.line = line;
this.symbol = methodName;
}
}
}
2 changes: 1 addition & 1 deletion src/main/java/com/squareup/squash/SquashEntry.java
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ public class SquashEntry {
private final String occurred_at;

// Used in tests.
final List<List<Object>> backtraces;
final List<SquashBacktrace.SquashException> backtraces;
final Map<String, Object> ivars;
final List<SquashBacktrace.NestedException> parent_exceptions;
final String class_name;
Expand Down
61 changes: 30 additions & 31 deletions src/test/java/com/squareup/squash/SquashEntryTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -60,10 +60,10 @@ private SquashEntry serializeAndDeserialize(SquashEntry logEntry) throws IOExcep
final SquashEntry logEntry = factory.create(logMessage, exception);
SquashEntry deserialized = serializeAndDeserialize(logEntry);
assertThat(deserialized.backtraces).isNotEmpty();
final List<Object> backtrace = deserialized.backtraces.get(0);
assertThat(backtrace.get(0)).isEqualTo(Thread.currentThread().getName());
assertThat(backtrace.get(1)).isEqualTo(true);
List<List<Object>> stackElements = (List<List<Object>>) backtrace.get(2);
final SquashBacktrace.SquashException backtrace = deserialized.backtraces.get(0);
assertThat(backtrace.name).isEqualTo(Thread.currentThread().getName());
assertThat(backtrace.faulted).isEqualTo(true);
List<SquashBacktrace.StackElement> stackElements = backtrace.backtrace;
assertBacktracesMatch(myLittleStackTrace, stackElements);
assertThat(deserialized.ivars).isEmpty();
assertThat(deserialized.log_message).isEqualTo(logMessage);
Expand All @@ -73,15 +73,14 @@ private SquashEntry serializeAndDeserialize(SquashEntry logEntry) throws IOExcep
}

private void assertBacktracesMatch(StackTraceElement[] myLittleStackTrace,
List<List<Object>> stackElements) {
List<SquashBacktrace.StackElement> stackElements) {
for (int i = 0, stackElementsSize = stackElements.size(); i < stackElementsSize; i++) {
List<Object> stackElement = stackElements.get(i);
SquashBacktrace.StackElement stackElement = stackElements.get(i);
StackTraceElement expected = myLittleStackTrace[i];
assertThat(stackElement.get(0)).isEqualTo(SquashBacktrace.JAVA_PREFIX);
assertThat(stackElement.get(1)).isEqualTo(expected.getFileName());
assertThat(((Double) stackElement.get(2)).intValue()).isEqualTo(expected.getLineNumber());
assertThat(stackElement.get(3)).isEqualTo(expected.getMethodName());
assertThat(stackElement.get(4)).isEqualTo(expected.getClassName());
assertThat(stackElement.file).isEqualTo(expected.getFileName());
assertThat(stackElement.line).isEqualTo(expected.getLineNumber());
assertThat(stackElement.symbol).isEqualTo(expected.getMethodName());
assertThat(stackElement.class_name).isEqualTo(expected.getClassName());
}
}

Expand Down Expand Up @@ -133,10 +132,10 @@ private void assertBacktracesMatch(StackTraceElement[] myLittleStackTrace,
final SquashEntry logEntry = factory.create(logMessage, exception);
SquashEntry deserialized = serializeAndDeserialize(logEntry);
assertThat(deserialized.backtraces).isNotEmpty();
List<Object> backtrace = deserialized.backtraces.get(0);
assertThat(backtrace.get(0)).isEqualTo(Thread.currentThread().getName());
assertThat(backtrace.get(1)).isEqualTo(true);
List<List<Object>> stackElements = (List<List<Object>>) backtrace.get(2);
SquashBacktrace.SquashException backtrace = deserialized.backtraces.get(0);
assertThat(backtrace.name).isEqualTo(Thread.currentThread().getName());
assertThat(backtrace.faulted).isEqualTo(true);
List<SquashBacktrace.StackElement> stackElements = backtrace.backtrace;
assertBacktracesMatch(myLittleStackTrace, stackElements);
assertThat(deserialized.ivars).isEmpty();
assertThat(deserialized.log_message).isEqualTo(logMessage);
Expand All @@ -149,18 +148,18 @@ private void assertBacktracesMatch(StackTraceElement[] myLittleStackTrace,
assertThat(nested1.ivars).isEmpty();
assertThat(nested1.message).isEqualTo(nestedExceptionMessage);
backtrace = nested1.backtraces.get(0);
assertThat(backtrace.get(0)).isEqualTo(Thread.currentThread().getName());
assertThat(backtrace.get(1)).isEqualTo(true);
assertBacktracesMatch(nestedStackTrace, (List<List<Object>>) backtrace.get(2));
assertThat(backtrace.name).isEqualTo(Thread.currentThread().getName());
assertThat(backtrace.faulted).isEqualTo(true);
assertBacktracesMatch(nestedStackTrace, backtrace.backtrace);

final SquashBacktrace.NestedException nested2 = nestedExceptions.get(1);
assertThat(nested2.class_name).isEqualTo(doublyNestedException.getClass().getName());
assertThat(nested2.ivars).isEmpty();
assertThat(nested2.message).isEqualTo(doublyNestedExceptionMessage);
backtrace = nested1.backtraces.get(0);
assertThat(backtrace.get(0)).isEqualTo(Thread.currentThread().getName());
assertThat(backtrace.get(1)).isEqualTo(true);
assertBacktracesMatch(nestedStackTrace, (List<List<Object>>) backtrace.get(2));
assertThat(backtrace.name).isEqualTo(Thread.currentThread().getName());
assertThat(backtrace.faulted).isEqualTo(true);
assertBacktracesMatch(nestedStackTrace, backtrace.backtrace);
}

@Test public void testInfinitelyNestedExceptions() throws Exception {
Expand Down Expand Up @@ -212,10 +211,10 @@ private void assertBacktracesMatch(StackTraceElement[] myLittleStackTrace,
final SquashEntry logEntry = factory.create(logMessage, exception);
SquashEntry deserialized = serializeAndDeserialize(logEntry);
assertThat(deserialized.backtraces).isNotEmpty();
List<Object> backtrace = deserialized.backtraces.get(0);
assertThat(backtrace.get(0)).isEqualTo(Thread.currentThread().getName());
assertThat(backtrace.get(1)).isEqualTo(true);
List<List<Object>> stackElements = (List<List<Object>>) backtrace.get(2);
SquashBacktrace.SquashException backtrace = deserialized.backtraces.get(0);
assertThat(backtrace.name).isEqualTo(Thread.currentThread().getName());
assertThat(backtrace.faulted).isEqualTo(true);
List<SquashBacktrace.StackElement> stackElements = backtrace.backtrace;
assertBacktracesMatch(myLittleStackTrace, stackElements);
assertThat(deserialized.ivars).isEmpty();
assertThat(deserialized.log_message).isEqualTo(logMessage);
Expand All @@ -228,18 +227,18 @@ private void assertBacktracesMatch(StackTraceElement[] myLittleStackTrace,
assertThat(nested1.ivars).isEmpty();
assertThat(nested1.message).isEqualTo(nestedExceptionMessage);
backtrace = nested1.backtraces.get(0);
assertThat(backtrace.get(0)).isEqualTo(Thread.currentThread().getName());
assertThat(backtrace.get(1)).isEqualTo(true);
assertBacktracesMatch(nestedStackTrace, (List<List<Object>>) backtrace.get(2));
assertThat(backtrace.name).isEqualTo(Thread.currentThread().getName());
assertThat(backtrace.faulted).isEqualTo(true);
assertBacktracesMatch(nestedStackTrace, backtrace.backtrace);

final SquashBacktrace.NestedException nested2 = nestedExceptions.get(1);
assertThat(nested2.class_name).isEqualTo(doublyNestedException.getClass().getName());
assertThat(nested2.ivars).isEmpty();
assertThat(nested2.message).isEqualTo(doublyNestedExceptionMessage);
backtrace = nested1.backtraces.get(0);
assertThat(backtrace.get(0)).isEqualTo(Thread.currentThread().getName());
assertThat(backtrace.get(1)).isEqualTo(true);
assertBacktracesMatch(nestedStackTrace, (List<List<Object>>) backtrace.get(2));
assertThat(backtrace.name).isEqualTo(Thread.currentThread().getName());
assertThat(backtrace.faulted).isEqualTo(true);
assertBacktracesMatch(nestedStackTrace, backtrace.backtrace);
}

private class EntryFactory {
Expand Down