Skip to content

Commit 4e23394

Browse files
author
Mikel Pascual
committed
Show relevant cause as root cause of the Throwable
1 parent 3e41638 commit 4e23394

File tree

3 files changed

+148
-60
lines changed

3 files changed

+148
-60
lines changed
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/*
2+
* Copyright 2017 akaita
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.akaita.java.rxjava2debug;
18+
19+
import io.reactivex.annotations.NonNull;
20+
21+
import java.util.LinkedList;
22+
import java.util.List;
23+
24+
class ExceptionUtils {
25+
26+
static Throwable setRootCause(@NonNull Throwable throwable, @NonNull Throwable rootCause) {
27+
if (throwable == null){
28+
return null;
29+
}
30+
if (rootCause == null) {
31+
return throwable;
32+
}
33+
34+
List<Throwable> causes = listCauses(throwable);
35+
causes.add(rootCause);
36+
return collapseCauses(causes);
37+
}
38+
39+
private static @NonNull
40+
List<Throwable> listCauses(@NonNull Throwable throwable) {
41+
LinkedList<Throwable> causes = new LinkedList<Throwable>();
42+
Throwable cause = throwable.getCause();
43+
while (cause != null && !causes.contains(cause)) {
44+
causes.add(cause);
45+
cause = cause.getCause();
46+
}
47+
return causes;
48+
}
49+
50+
private static @NonNull
51+
Throwable collapseCauses(@NonNull List<Throwable> causes) {
52+
if (causes.size() == 0) {
53+
return new RuntimeException("Empty list of causes");
54+
}
55+
56+
Throwable topThrowable = null;
57+
for (int i=causes.size()-1 ; i>=0 ; i--) {
58+
topThrowable = new Throwable(causes.get(i).getMessage(), topThrowable);
59+
topThrowable.setStackTrace(causes.get(i).getStackTrace());
60+
}
61+
62+
return topThrowable;
63+
}
64+
}

src/main/java/com/akaita/java/rxjava2debug/RxJava2Debug.java

Lines changed: 5 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -18,19 +18,11 @@
1818

1919
import hu.akarnokd.rxjava2.debug.RxJavaAssemblyException;
2020
import hu.akarnokd.rxjava2.debug.RxJavaAssemblyTracking;
21-
import io.reactivex.annotations.NonNull;
22-
import io.reactivex.annotations.Nullable;
2321

24-
import java.util.ArrayList;
25-
import java.util.List;
26-
import java.util.regex.Matcher;
27-
import java.util.regex.Pattern;
22+
import static com.akaita.java.rxjava2debug.ExceptionUtils.setRootCause;
23+
import static com.akaita.java.rxjava2debug.StackTraceUtils.parseStackTrace;
2824

2925
public class RxJava2Debug {
30-
//Example: "at com.akaita.fgas.MainActivity.onCreate(MainActivity.java:210)"
31-
private static final Pattern STACK_TRACE_ELEMENT_PATTERN = Pattern.compile("^at (.*)\\.(.*)\\((.*):([0-9]+)\\)$");
32-
private static final String NEW_LINE_REGEX = "\\n\\r|\\r\\n|\\n|\\r";
33-
3426
private static String BASE_APP_PACKAGE_NAME = "com.akaita.fgas";
3527

3628
/**
@@ -54,62 +46,15 @@ public void uncaughtException(Thread t, Throwable e) {
5446

5547
RxJavaAssemblyException assembledException = RxJavaAssemblyException.find(e);
5648
if (assembledException != null) {
57-
StackTraceElement[] clearStack = parseStackTrace(assembledException);
58-
Throwable clearException = new Throwable(e);
49+
StackTraceElement[] clearStack = parseStackTrace(assembledException, BASE_APP_PACKAGE_NAME);
50+
Throwable clearException = new Throwable();
5951
clearException.setStackTrace(clearStack);
60-
toThrow = clearException;
52+
toThrow = setRootCause(e, clearException);
6153
}
6254

6355
previousDefaultHandler.uncaughtException(t, toThrow);
6456
}
6557
});
6658
}
6759

68-
/**
69-
* Extract StackTrace and filter to show an app-specific entry at its top
70-
*
71-
* @param exception RxJavaAssemblyException to be parsed
72-
* @return StackTrace, filtered so a app-specific line is at the top of it
73-
*/
74-
private @NonNull
75-
static StackTraceElement[] parseStackTrace(@NonNull RxJavaAssemblyException exception) {
76-
String[] lines = exception.stacktrace()
77-
.split(NEW_LINE_REGEX);
78-
79-
List<StackTraceElement> stackTrace = new ArrayList<StackTraceElement>();
80-
boolean appSpecificFound = false;
81-
for (String line : lines) {
82-
appSpecificFound = appSpecificFound || line.contains(BASE_APP_PACKAGE_NAME);
83-
if (appSpecificFound) {
84-
StackTraceElement element = parseStackTraceLine(line);
85-
if (element != null) {
86-
stackTrace.add(element);
87-
}
88-
}
89-
}
90-
91-
return stackTrace.toArray(new StackTraceElement[0]);
92-
}
93-
94-
/**
95-
* Parse string containing a <i>single line</i> of a StackTrace
96-
*
97-
* @param stackTraceLine string containing single line of a StackTrace
98-
* @return parsed StackTraceElement
99-
*/
100-
private @Nullable
101-
static StackTraceElement parseStackTraceLine(@NonNull String stackTraceLine) {
102-
StackTraceElement retVal = null;
103-
104-
Matcher matcher = STACK_TRACE_ELEMENT_PATTERN.matcher(stackTraceLine);
105-
if (matcher.matches()) {
106-
String clazz = matcher.group(1);
107-
String method = matcher.group(2);
108-
String filename = matcher.group(3);
109-
int line = Integer.valueOf(matcher.group(4));
110-
retVal = new StackTraceElement(clazz, method, filename, line);
111-
}
112-
return retVal;
113-
}
114-
11560
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
/*
2+
* Copyright 2017 akaita
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.akaita.java.rxjava2debug;
18+
19+
import hu.akarnokd.rxjava2.debug.RxJavaAssemblyException;
20+
import io.reactivex.annotations.NonNull;
21+
import io.reactivex.annotations.Nullable;
22+
23+
import java.util.ArrayList;
24+
import java.util.List;
25+
import java.util.regex.Matcher;
26+
import java.util.regex.Pattern;
27+
28+
class StackTraceUtils {
29+
//Example: "at com.akaita.fgas.MainActivity.onCreate(MainActivity.java:210)"
30+
private static final Pattern STACK_TRACE_ELEMENT_PATTERN = Pattern.compile("^at (.*)\\.(.*)\\((.*):([0-9]+)\\)$");
31+
private static final String NEW_LINE_REGEX = "\\n\\r|\\r\\n|\\n|\\r";
32+
33+
/**
34+
* Extract StackTrace and filter to show an app-specific entry at its top
35+
*
36+
* @param exception RxJavaAssemblyException to be parsed
37+
* @return StackTrace, filtered so a app-specific line is at the top of it
38+
*/
39+
@NonNull
40+
static StackTraceElement[] parseStackTrace(@NonNull RxJavaAssemblyException exception, @NonNull String basePackageName) {
41+
String[] lines = exception.stacktrace()
42+
.split(NEW_LINE_REGEX);
43+
44+
List<StackTraceElement> stackTrace = new ArrayList<StackTraceElement>();
45+
boolean appSpecificFound = false;
46+
for (String line : lines) {
47+
appSpecificFound = appSpecificFound || line.contains(basePackageName);
48+
if (appSpecificFound) {
49+
StackTraceElement element = parseStackTraceLine(line);
50+
if (element != null) {
51+
stackTrace.add(element);
52+
}
53+
}
54+
}
55+
56+
return stackTrace.toArray(new StackTraceElement[0]);
57+
}
58+
59+
/**
60+
* Parse string containing a <i>single line</i> of a StackTrace
61+
*
62+
* @param stackTraceLine string containing single line of a StackTrace
63+
* @return parsed StackTraceElement
64+
*/
65+
@Nullable
66+
private static StackTraceElement parseStackTraceLine(@NonNull String stackTraceLine) {
67+
StackTraceElement retVal = null;
68+
69+
Matcher matcher = STACK_TRACE_ELEMENT_PATTERN.matcher(stackTraceLine);
70+
if (matcher.matches()) {
71+
String clazz = matcher.group(1);
72+
String method = matcher.group(2);
73+
String filename = matcher.group(3);
74+
int line = Integer.valueOf(matcher.group(4));
75+
retVal = new StackTraceElement(clazz, method, filename, line);
76+
}
77+
return retVal;
78+
}
79+
}

0 commit comments

Comments
 (0)