-
Notifications
You must be signed in to change notification settings - Fork 329
Expand file tree
/
Copy pathExceptionHelper.java
More file actions
135 lines (122 loc) · 4.04 KB
/
ExceptionHelper.java
File metadata and controls
135 lines (122 loc) · 4.04 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
package com.datadog.debugger.util;
import datadog.logging.RatelimitedLogger;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Deque;
import java.util.List;
import org.slf4j.Logger;
/** Helper class for rate limiting & logging exceptions */
public class ExceptionHelper {
private static final Object[] EMPTY_ARRAY = new Object[0];
public static void logException(Logger log, Throwable ex, String message, Object... params) {
if (log.isDebugEnabled()) {
if (params == null) {
params = EMPTY_ARRAY;
}
Object[] fullParams = Arrays.copyOf(params, params.length + 1);
fullParams[params.length] = ex;
log.debug(message, fullParams);
} else {
log.warn(message + " " + ex.toString(), params);
}
}
public static void rateLimitedLogException(
RatelimitedLogger ratelimitedLogger,
Logger log,
Throwable ex,
String message,
Object... params) {
if (log.isDebugEnabled()) {
logException(log, ex, message, params);
} else {
ratelimitedLogger.warn(message + " " + ex.toString(), params);
}
}
public static String foldExceptionStackTrace(Throwable t) {
StringWriter writer = new StringWriter();
t.printStackTrace(new NoNewLinePrintWriter(writer));
return writer.toString();
}
private static class NoNewLinePrintWriter extends PrintWriter {
public NoNewLinePrintWriter(Writer out) {
super(out);
}
@Override
public void println() {}
@Override
public void write(String s, int off, int len) {
super.write(s.replace('\t', ' '), off, len);
}
}
public static Throwable getInnerMostThrowable(Throwable t) {
return getInnerMostThrowable(t, null);
}
public static Throwable getInnerMostThrowable(Throwable t, Deque<Throwable> chainedExceptions) {
if (chainedExceptions != null) {
chainedExceptions.addFirst(t);
}
// putting an arbitrary limit to avoid infinite loops with cycling causes
int i = 100;
while (t.getCause() != null && i > 0) {
t = t.getCause();
if (chainedExceptions != null) {
chainedExceptions.addFirst(t);
}
i--;
}
if (i == 0) {
return null;
}
return t;
}
public static StackTraceElement[] flattenStackTrace(Throwable t) {
List<StackTraceElement> result = new ArrayList<>();
result.addAll(Arrays.asList(t.getStackTrace()));
if (t.getCause() != null) {
internalFlattenStackTrace(t.getCause(), t.getStackTrace(), result);
}
return result.toArray(new StackTraceElement[0]);
}
private static void internalFlattenStackTrace(
Throwable t, StackTraceElement[] enclosingTrace, List<StackTraceElement> elements) {
StackTraceElement[] trace = t.getStackTrace();
int m = trace.length - 1;
int n = enclosingTrace.length - 1;
while (m >= 0 && n >= 0 && trace[m].equals(enclosingTrace[n])) {
m--;
n--;
}
for (int i = 0; i <= m; i++) {
elements.add(trace[i]);
}
if (t.getCause() != null) {
internalFlattenStackTrace(t.getCause(), trace, elements);
}
}
// Because flattened stack traces are organized with first frames at the bottom I need to follow
// the order of the first frame and wrap around the array with a modulo to continue the matching
public static int[] createThrowableMapping(Throwable innerMost, Throwable current) {
StackTraceElement[] innerTrace = innerMost.getStackTrace();
StackTraceElement[] currentTrace = flattenStackTrace(current);
int[] mapping = new int[innerTrace.length];
int currentIdx = 0;
for (int i = 0; i < innerTrace.length; i++) {
mapping[i] = -1;
int count = currentTrace.length;
int idx = currentIdx;
while (count > 0) {
if (innerTrace[i].equals(currentTrace[idx])) {
mapping[i] = idx;
currentIdx = (idx + 1) % currentTrace.length;
break;
}
idx = (idx + 1) % currentTrace.length;
count--;
}
}
return mapping;
}
}