Skip to content

Commit 5ee62f9

Browse files
committed
Merge pull request #3 from SquareSquash/edenman/new-hash-format
Switch to new hash-based backtrace format
2 parents c30862d + c178d7e commit 5ee62f9

File tree

3 files changed

+78
-54
lines changed

3 files changed

+78
-54
lines changed

src/main/java/com/squareup/squash/SquashBacktrace.java

Lines changed: 47 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -24,35 +24,27 @@
2424
/** Creates the Squash stacktrace format for serialization by gson. */
2525
public final class SquashBacktrace {
2626

27-
// This lets Squash know that the stacktrace is java-style and can contain obfuscated classes.
28-
public static final String JAVA_PREFIX = "_JAVA_";
29-
3027
private SquashBacktrace() {
3128
// Should not be instantiated: this is a utility class.
3229
}
3330

34-
public static List<List<Object>> getBacktraces(Throwable error) {
31+
public static List<SquashException> getBacktraces(Throwable error) {
3532
if (error == null) {
3633
return null;
3734
}
38-
final ArrayList<List<Object>> threadList = new ArrayList<List<Object>>();
39-
final ArrayList<Object> currentThread = new ArrayList<Object>();
40-
currentThread.add(Thread.currentThread().getName());
41-
currentThread.add(true);
42-
currentThread.add(getStacktraceArray(error));
35+
final List<SquashException> threadList = new ArrayList<SquashException>();
36+
final SquashException currentThread =
37+
new SquashException(Thread.currentThread().getName(), true, getStacktraceArray(error));
4338
threadList.add(currentThread);
4439
return threadList;
4540
}
4641

47-
private static List<List<Object>> getStacktraceArray(Throwable error) {
48-
List<List<Object>> stackElems = new ArrayList<List<Object>>();
42+
private static List<StackElement> getStacktraceArray(Throwable error) {
43+
List<StackElement> stackElems = new ArrayList<StackElement>();
4944
for (StackTraceElement element : error.getStackTrace()) {
50-
List<Object> elementList = new ArrayList<Object>();
51-
elementList.add(JAVA_PREFIX);
52-
elementList.add(element.getFileName());
53-
elementList.add(element.getLineNumber());
54-
elementList.add(element.getMethodName());
55-
elementList.add(element.getClassName());
45+
StackElement elementList =
46+
new StackElement(element.getClassName(), element.getFileName(), element.getLineNumber(),
47+
element.getMethodName());
5648
stackElems.add(elementList);
5749
}
5850
return stackElems;
@@ -92,26 +84,59 @@ public static void populateNestedExceptions(List<NestedException> nestedExceptio
9284
return;
9385
}
9486
final Throwable cause = error.getCause();
95-
NestedException doc = new NestedException(cause.getClass().getName(), cause.getMessage(),
96-
getBacktraces(cause), getIvars(cause));
87+
NestedException doc =
88+
new NestedException(cause.getClass().getName(), cause.getMessage(), getBacktraces(cause),
89+
getIvars(cause));
9790
nestedExceptions.add(doc);
9891
// Exceptions all the way down!
9992
populateNestedExceptions(nestedExceptions, cause);
10093
}
10194

95+
/** Wrapper object for top-level exceptions. */
96+
static final class SquashException {
97+
final String name;
98+
final boolean faulted;
99+
final List<StackElement> backtrace;
100+
101+
public SquashException(String name, boolean faulted, List<StackElement> backtrace) {
102+
this.backtrace = backtrace;
103+
this.name = name;
104+
this.faulted = faulted;
105+
}
106+
}
107+
102108
/** Wrapper object for nested exceptions. */
103-
public static class NestedException {
109+
static final class NestedException {
104110
final String class_name;
105111
final String message;
106-
final List<List<Object>> backtraces;
112+
final List<SquashException> backtraces;
107113
final Map<String, Object> ivars;
108114

109-
public NestedException(String className, String message, List<List<Object>> backtraces,
115+
public NestedException(String className, String message, List<SquashException> backtraces,
110116
Map<String, Object> ivars) {
111117
this.class_name = className;
112118
this.message = message;
113119
this.backtraces = backtraces;
114120
this.ivars = ivars;
115121
}
116122
}
123+
124+
/** Wrapper object for a stacktrace entry. */
125+
static final class StackElement {
126+
// This field is necessary so Squash knows that this is a java stacktrace that might need
127+
// obfuscation lookup and git filename lookup. Our stacktrace elements don't give us the full
128+
// path to the java file, so Squash has to do a SCM lookup to try and do its cause analysis.
129+
@SuppressWarnings("UnusedDeclaration") final String type = "obfuscated";
130+
final String file;
131+
final int line;
132+
final String symbol;
133+
final String class_name;
134+
135+
private StackElement(String className, String file, int line, String methodName) {
136+
this.class_name = className;
137+
this.file = file;
138+
this.line = line;
139+
this.symbol = methodName;
140+
}
141+
}
117142
}

src/main/java/com/squareup/squash/SquashEntry.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ public class SquashEntry {
4646
private final String occurred_at;
4747

4848
// Used in tests.
49-
final List<List<Object>> backtraces;
49+
final List<SquashBacktrace.SquashException> backtraces;
5050
final Map<String, Object> ivars;
5151
final List<SquashBacktrace.NestedException> parent_exceptions;
5252
final String class_name;

src/test/java/com/squareup/squash/SquashEntryTest.java

Lines changed: 30 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -60,10 +60,10 @@ private SquashEntry serializeAndDeserialize(SquashEntry logEntry) throws IOExcep
6060
final SquashEntry logEntry = factory.create(logMessage, exception);
6161
SquashEntry deserialized = serializeAndDeserialize(logEntry);
6262
assertThat(deserialized.backtraces).isNotEmpty();
63-
final List<Object> backtrace = deserialized.backtraces.get(0);
64-
assertThat(backtrace.get(0)).isEqualTo(Thread.currentThread().getName());
65-
assertThat(backtrace.get(1)).isEqualTo(true);
66-
List<List<Object>> stackElements = (List<List<Object>>) backtrace.get(2);
63+
final SquashBacktrace.SquashException backtrace = deserialized.backtraces.get(0);
64+
assertThat(backtrace.name).isEqualTo(Thread.currentThread().getName());
65+
assertThat(backtrace.faulted).isEqualTo(true);
66+
List<SquashBacktrace.StackElement> stackElements = backtrace.backtrace;
6767
assertBacktracesMatch(myLittleStackTrace, stackElements);
6868
assertThat(deserialized.ivars).isEmpty();
6969
assertThat(deserialized.log_message).isEqualTo(logMessage);
@@ -73,15 +73,14 @@ private SquashEntry serializeAndDeserialize(SquashEntry logEntry) throws IOExcep
7373
}
7474

7575
private void assertBacktracesMatch(StackTraceElement[] myLittleStackTrace,
76-
List<List<Object>> stackElements) {
76+
List<SquashBacktrace.StackElement> stackElements) {
7777
for (int i = 0, stackElementsSize = stackElements.size(); i < stackElementsSize; i++) {
78-
List<Object> stackElement = stackElements.get(i);
78+
SquashBacktrace.StackElement stackElement = stackElements.get(i);
7979
StackTraceElement expected = myLittleStackTrace[i];
80-
assertThat(stackElement.get(0)).isEqualTo(SquashBacktrace.JAVA_PREFIX);
81-
assertThat(stackElement.get(1)).isEqualTo(expected.getFileName());
82-
assertThat(((Double) stackElement.get(2)).intValue()).isEqualTo(expected.getLineNumber());
83-
assertThat(stackElement.get(3)).isEqualTo(expected.getMethodName());
84-
assertThat(stackElement.get(4)).isEqualTo(expected.getClassName());
80+
assertThat(stackElement.file).isEqualTo(expected.getFileName());
81+
assertThat(stackElement.line).isEqualTo(expected.getLineNumber());
82+
assertThat(stackElement.symbol).isEqualTo(expected.getMethodName());
83+
assertThat(stackElement.class_name).isEqualTo(expected.getClassName());
8584
}
8685
}
8786

@@ -133,10 +132,10 @@ private void assertBacktracesMatch(StackTraceElement[] myLittleStackTrace,
133132
final SquashEntry logEntry = factory.create(logMessage, exception);
134133
SquashEntry deserialized = serializeAndDeserialize(logEntry);
135134
assertThat(deserialized.backtraces).isNotEmpty();
136-
List<Object> backtrace = deserialized.backtraces.get(0);
137-
assertThat(backtrace.get(0)).isEqualTo(Thread.currentThread().getName());
138-
assertThat(backtrace.get(1)).isEqualTo(true);
139-
List<List<Object>> stackElements = (List<List<Object>>) backtrace.get(2);
135+
SquashBacktrace.SquashException backtrace = deserialized.backtraces.get(0);
136+
assertThat(backtrace.name).isEqualTo(Thread.currentThread().getName());
137+
assertThat(backtrace.faulted).isEqualTo(true);
138+
List<SquashBacktrace.StackElement> stackElements = backtrace.backtrace;
140139
assertBacktracesMatch(myLittleStackTrace, stackElements);
141140
assertThat(deserialized.ivars).isEmpty();
142141
assertThat(deserialized.log_message).isEqualTo(logMessage);
@@ -149,18 +148,18 @@ private void assertBacktracesMatch(StackTraceElement[] myLittleStackTrace,
149148
assertThat(nested1.ivars).isEmpty();
150149
assertThat(nested1.message).isEqualTo(nestedExceptionMessage);
151150
backtrace = nested1.backtraces.get(0);
152-
assertThat(backtrace.get(0)).isEqualTo(Thread.currentThread().getName());
153-
assertThat(backtrace.get(1)).isEqualTo(true);
154-
assertBacktracesMatch(nestedStackTrace, (List<List<Object>>) backtrace.get(2));
151+
assertThat(backtrace.name).isEqualTo(Thread.currentThread().getName());
152+
assertThat(backtrace.faulted).isEqualTo(true);
153+
assertBacktracesMatch(nestedStackTrace, backtrace.backtrace);
155154

156155
final SquashBacktrace.NestedException nested2 = nestedExceptions.get(1);
157156
assertThat(nested2.class_name).isEqualTo(doublyNestedException.getClass().getName());
158157
assertThat(nested2.ivars).isEmpty();
159158
assertThat(nested2.message).isEqualTo(doublyNestedExceptionMessage);
160159
backtrace = nested1.backtraces.get(0);
161-
assertThat(backtrace.get(0)).isEqualTo(Thread.currentThread().getName());
162-
assertThat(backtrace.get(1)).isEqualTo(true);
163-
assertBacktracesMatch(nestedStackTrace, (List<List<Object>>) backtrace.get(2));
160+
assertThat(backtrace.name).isEqualTo(Thread.currentThread().getName());
161+
assertThat(backtrace.faulted).isEqualTo(true);
162+
assertBacktracesMatch(nestedStackTrace, backtrace.backtrace);
164163
}
165164

166165
@Test public void testInfinitelyNestedExceptions() throws Exception {
@@ -212,10 +211,10 @@ private void assertBacktracesMatch(StackTraceElement[] myLittleStackTrace,
212211
final SquashEntry logEntry = factory.create(logMessage, exception);
213212
SquashEntry deserialized = serializeAndDeserialize(logEntry);
214213
assertThat(deserialized.backtraces).isNotEmpty();
215-
List<Object> backtrace = deserialized.backtraces.get(0);
216-
assertThat(backtrace.get(0)).isEqualTo(Thread.currentThread().getName());
217-
assertThat(backtrace.get(1)).isEqualTo(true);
218-
List<List<Object>> stackElements = (List<List<Object>>) backtrace.get(2);
214+
SquashBacktrace.SquashException backtrace = deserialized.backtraces.get(0);
215+
assertThat(backtrace.name).isEqualTo(Thread.currentThread().getName());
216+
assertThat(backtrace.faulted).isEqualTo(true);
217+
List<SquashBacktrace.StackElement> stackElements = backtrace.backtrace;
219218
assertBacktracesMatch(myLittleStackTrace, stackElements);
220219
assertThat(deserialized.ivars).isEmpty();
221220
assertThat(deserialized.log_message).isEqualTo(logMessage);
@@ -228,18 +227,18 @@ private void assertBacktracesMatch(StackTraceElement[] myLittleStackTrace,
228227
assertThat(nested1.ivars).isEmpty();
229228
assertThat(nested1.message).isEqualTo(nestedExceptionMessage);
230229
backtrace = nested1.backtraces.get(0);
231-
assertThat(backtrace.get(0)).isEqualTo(Thread.currentThread().getName());
232-
assertThat(backtrace.get(1)).isEqualTo(true);
233-
assertBacktracesMatch(nestedStackTrace, (List<List<Object>>) backtrace.get(2));
230+
assertThat(backtrace.name).isEqualTo(Thread.currentThread().getName());
231+
assertThat(backtrace.faulted).isEqualTo(true);
232+
assertBacktracesMatch(nestedStackTrace, backtrace.backtrace);
234233

235234
final SquashBacktrace.NestedException nested2 = nestedExceptions.get(1);
236235
assertThat(nested2.class_name).isEqualTo(doublyNestedException.getClass().getName());
237236
assertThat(nested2.ivars).isEmpty();
238237
assertThat(nested2.message).isEqualTo(doublyNestedExceptionMessage);
239238
backtrace = nested1.backtraces.get(0);
240-
assertThat(backtrace.get(0)).isEqualTo(Thread.currentThread().getName());
241-
assertThat(backtrace.get(1)).isEqualTo(true);
242-
assertBacktracesMatch(nestedStackTrace, (List<List<Object>>) backtrace.get(2));
239+
assertThat(backtrace.name).isEqualTo(Thread.currentThread().getName());
240+
assertThat(backtrace.faulted).isEqualTo(true);
241+
assertBacktracesMatch(nestedStackTrace, backtrace.backtrace);
243242
}
244243

245244
private class EntryFactory {

0 commit comments

Comments
 (0)