1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.logging.log4j.core.impl;
18
19 import java.io.Serializable;
20 import java.lang.reflect.Method;
21 import java.net.URL;
22 import java.security.CodeSource;
23 import java.util.HashMap;
24 import java.util.List;
25 import java.util.Map;
26 import java.util.Stack;
27
28 import org.apache.logging.log4j.Logger;
29 import org.apache.logging.log4j.status.StatusLogger;
30
31
32
33
34 public class ThrowableProxy implements Serializable {
35
36 private static final long serialVersionUID = -2752771578252251910L;
37
38 private static final Logger LOGGER = StatusLogger.getLogger();
39
40 private static final PrivateSecurityManager SECURITY_MANAGER;
41
42 private static final Method GET_SUPPRESSED;
43
44 private static final Method ADD_SUPPRESSED;
45
46 private final ThrowableProxy proxyCause;
47
48 private final Throwable throwable;
49
50 private final String name;
51
52 private final StackTracePackageElement[] callerPackageData;
53
54 private int commonElementCount;
55
56 static {
57 if (ReflectiveCallerClassUtility.isSupported()) {
58 SECURITY_MANAGER = null;
59 } else {
60 PrivateSecurityManager securityManager;
61 try {
62 securityManager = new PrivateSecurityManager();
63 if (securityManager.getClasses() == null) {
64
65 securityManager = null;
66 LOGGER.error("Unable to obtain call stack from security manager.");
67 }
68 } catch (final Exception e) {
69 securityManager = null;
70 LOGGER.debug("Unable to install security manager.", e);
71 }
72 SECURITY_MANAGER = securityManager;
73 }
74
75 Method getSuppressed = null, addSuppressed = null;
76 final Method[] methods = Throwable.class.getMethods();
77 for (final Method method : methods) {
78 if (method.getName().equals("getSuppressed")) {
79 getSuppressed = method;
80 } else if (method.getName().equals("addSuppressed")) {
81 addSuppressed = method;
82 }
83 }
84 GET_SUPPRESSED = getSuppressed;
85 ADD_SUPPRESSED = addSuppressed;
86 }
87
88
89
90
91
92 public ThrowableProxy(final Throwable throwable) {
93 this.throwable = throwable;
94 this.name = throwable.getClass().getName();
95 final Map<String, CacheEntry> map = new HashMap<String, CacheEntry>();
96 final Stack<Class<?>> stack = getCurrentStack();
97 callerPackageData = resolvePackageData(stack, map, null, throwable.getStackTrace());
98 this.proxyCause = throwable.getCause() == null ? null :
99 new ThrowableProxy(throwable, stack, map, throwable.getCause());
100 setSuppressed(throwable);
101 }
102
103
104
105
106
107
108
109
110
111 private ThrowableProxy(final Throwable parent, final Stack<Class<?>> stack, final Map<String, CacheEntry> map,
112 final Throwable cause) {
113 this.throwable = cause;
114 this.name = cause.getClass().getName();
115 callerPackageData = resolvePackageData(stack, map, parent.getStackTrace(), cause.getStackTrace());
116 this.proxyCause = cause.getCause() == null ? null :
117 new ThrowableProxy(parent, stack, map, cause.getCause());
118 setSuppressed(cause);
119 }
120
121 public Throwable getThrowable() {
122 return throwable;
123 }
124
125 public ThrowableProxy getCause() {
126 return proxyCause;
127 }
128
129
130
131
132
133 public String getName() {
134 return name;
135 }
136
137
138
139
140
141
142 public int getCommonElementCount() {
143 return commonElementCount;
144 }
145
146
147
148
149
150 public StackTracePackageElement[] getPackageData() {
151 return callerPackageData;
152 }
153
154 @Override
155 public String toString() {
156 final String msg = throwable.getMessage();
157 return msg != null ? name + ": " + msg : name;
158 }
159
160
161
162
163
164 public String getRootCauseStackTrace() {
165 return getRootCauseStackTrace(null);
166 }
167
168
169
170
171
172
173 public String getRootCauseStackTrace(final List<String> packages) {
174 final StringBuilder sb = new StringBuilder();
175 if (proxyCause != null) {
176 formatWrapper(sb, proxyCause);
177 sb.append("Wrapped by: ");
178 }
179 sb.append(toString());
180 sb.append("\n");
181 formatElements(sb, 0, throwable.getStackTrace(), callerPackageData, packages);
182 return sb.toString();
183 }
184
185
186
187
188
189
190 public void formatWrapper(final StringBuilder sb, final ThrowableProxy cause) {
191 formatWrapper(sb, cause, null);
192 }
193
194
195
196
197
198
199
200 @SuppressWarnings("ThrowableResultOfMethodCallIgnored")
201 public void formatWrapper(final StringBuilder sb, final ThrowableProxy cause, final List<String> packages) {
202 final Throwable caused = cause.getCause() != null ? cause.getCause().getThrowable() : null;
203 if (caused != null) {
204 formatWrapper(sb, cause.proxyCause);
205 sb.append("Wrapped by: ");
206 }
207 sb.append(cause).append("\n");
208 formatElements(sb, cause.commonElementCount, cause.getThrowable().getStackTrace(), cause.callerPackageData,
209 packages);
210 }
211
212
213
214
215
216 public String getExtendedStackTrace() {
217 return getExtendedStackTrace(null);
218 }
219
220
221
222
223
224
225 public String getExtendedStackTrace(final List<String> packages) {
226 final StringBuilder sb = new StringBuilder(name);
227 final String msg = throwable.getMessage();
228 if (msg != null) {
229 sb.append(": ").append(throwable.getMessage());
230 }
231 sb.append("\n");
232 formatElements(sb, 0, throwable.getStackTrace(), callerPackageData, packages);
233 if (proxyCause != null) {
234 formatCause(sb, proxyCause, packages);
235 }
236 return sb.toString();
237 }
238
239
240
241
242
243 public String getSuppressedStackTrace() {
244 final ThrowableProxy[] suppressed = getSuppressed();
245 if (suppressed == null || suppressed.length == 0) {
246 return "";
247 }
248 final StringBuilder sb = new StringBuilder("Suppressed Stack Trace Elements:\n");
249 for (final ThrowableProxy proxy : suppressed) {
250 sb.append(proxy.getExtendedStackTrace());
251 }
252 return sb.toString();
253 }
254
255 @SuppressWarnings("ThrowableResultOfMethodCallIgnored")
256 private void formatCause(final StringBuilder sb, final ThrowableProxy cause, final List<String> packages) {
257 sb.append("Caused by: ").append(cause).append("\n");
258 formatElements(sb, cause.commonElementCount, cause.getThrowable().getStackTrace(), cause.callerPackageData,
259 packages);
260 if (cause.getCause() != null) {
261 formatCause(sb, cause.proxyCause, packages);
262 }
263 }
264
265 private void formatElements(final StringBuilder sb, final int commonCount, final StackTraceElement[] causedTrace,
266 final StackTracePackageElement[] packageData, final List<String> packages) {
267 if (packages == null || packages.size() == 0) {
268 for (int i = 0; i < packageData.length; ++i) {
269 formatEntry(causedTrace[i], packageData[i], sb);
270 }
271 } else {
272 int count = 0;
273 for (int i = 0; i < packageData.length; ++i) {
274 if (!isSuppressed(causedTrace[i], packages)) {
275 if (count > 0) {
276 if (count == 1) {
277 sb.append("\t....\n");
278 } else {
279 sb.append("\t... suppressed ").append(count).append(" lines\n");
280 }
281 count = 0;
282 }
283 formatEntry(causedTrace[i], packageData[i], sb);
284 } else {
285 ++count;
286 }
287 }
288 if (count > 0) {
289 if (count == 1) {
290 sb.append("\t...\n");
291 } else {
292 sb.append("\t... suppressed ").append(count).append(" lines\n");
293 }
294 }
295 }
296 if (commonCount != 0) {
297 sb.append("\t... ").append(commonCount).append(" more").append("\n");
298 }
299 }
300
301 private void formatEntry(final StackTraceElement element, final StackTracePackageElement packageData,
302 final StringBuilder sb) {
303 sb.append("\tat ");
304 sb.append(element);
305 sb.append(" ");
306 sb.append(packageData);
307 sb.append("\n");
308 }
309
310 private boolean isSuppressed(final StackTraceElement element, final List<String> packages) {
311 final String className = element.getClassName();
312 for (final String pkg : packages) {
313 if (className.startsWith(pkg)) {
314 return true;
315 }
316 }
317 return false;
318 }
319
320
321
322
323
324
325
326 private Stack<Class<?>> getCurrentStack() {
327 if (ReflectiveCallerClassUtility.isSupported()) {
328 final Stack<Class<?>> classes = new Stack<Class<?>>();
329 int index = 1;
330 Class<?> clazz = ReflectiveCallerClassUtility.getCaller(index);
331 while (clazz != null) {
332 classes.push(clazz);
333 clazz = ReflectiveCallerClassUtility.getCaller(++index);
334 }
335 return classes;
336 } else if (SECURITY_MANAGER != null) {
337 final Class<?>[] array = SECURITY_MANAGER.getClasses();
338 final Stack<Class<?>> classes = new Stack<Class<?>>();
339 for (final Class<?> clazz : array) {
340 classes.push(clazz);
341 }
342 return classes;
343 }
344 return new Stack<Class<?>>();
345 }
346
347
348
349
350
351
352
353
354
355 StackTracePackageElement[] resolvePackageData(final Stack<Class<?>> stack, final Map<String,
356 CacheEntry> map,
357 final StackTraceElement[] rootTrace,
358 final StackTraceElement[] stackTrace) {
359 int stackLength;
360 if (rootTrace != null) {
361 int rootIndex = rootTrace.length - 1;
362 int stackIndex = stackTrace.length - 1;
363 while (rootIndex >= 0 && stackIndex >= 0 && rootTrace[rootIndex].equals(stackTrace[stackIndex])) {
364 --rootIndex;
365 --stackIndex;
366 }
367 commonElementCount = stackTrace.length - 1 - stackIndex;
368 stackLength = stackIndex + 1;
369 } else {
370 commonElementCount = 0;
371 stackLength = stackTrace.length;
372 }
373 final StackTracePackageElement[] packageArray = new StackTracePackageElement[stackLength];
374 Class<?> clazz = stack.isEmpty() ? null : stack.peek();
375 ClassLoader lastLoader = null;
376 for (int i = stackLength - 1; i >= 0; --i) {
377 final String className = stackTrace[i].getClassName();
378
379
380
381 if (clazz != null && className.equals(clazz.getName())) {
382 final CacheEntry entry = resolvePackageElement(clazz, true);
383 packageArray[i] = entry.element;
384 lastLoader = entry.loader;
385 stack.pop();
386 clazz = stack.isEmpty() ? null : stack.peek();
387 } else {
388 if (map.containsKey(className)) {
389 final CacheEntry entry = map.get(className);
390 packageArray[i] = entry.element;
391 if (entry.loader != null) {
392 lastLoader = entry.loader;
393 }
394 } else {
395 final CacheEntry entry = resolvePackageElement(loadClass(lastLoader, className), false);
396 packageArray[i] = entry.element;
397 map.put(className, entry);
398 if (entry.loader != null) {
399 lastLoader = entry.loader;
400 }
401 }
402 }
403 }
404 return packageArray;
405 }
406
407
408
409
410
411
412
413
414 private CacheEntry resolvePackageElement(final Class<?> callerClass, final boolean exact) {
415 String location = "?";
416 String version = "?";
417 ClassLoader lastLoader = null;
418 if (callerClass != null) {
419 try {
420 final CodeSource source = callerClass.getProtectionDomain().getCodeSource();
421 if (source != null) {
422 final URL locationURL = source.getLocation();
423 if (locationURL != null) {
424 final String str = locationURL.toString().replace('\\', '/');
425 int index = str.lastIndexOf("/");
426 if (index >= 0 && index == str.length() - 1) {
427 index = str.lastIndexOf("/", index - 1);
428 location = str.substring(index + 1);
429 } else {
430 location = str.substring(index + 1);
431 }
432 }
433 }
434 } catch (final Exception ex) {
435
436 }
437 final Package pkg = callerClass.getPackage();
438 if (pkg != null) {
439 final String ver = pkg.getImplementationVersion();
440 if (ver != null) {
441 version = ver;
442 }
443 }
444 lastLoader = callerClass.getClassLoader();
445 }
446 return new CacheEntry(new StackTracePackageElement(location, version, exact), lastLoader);
447 }
448
449
450
451
452
453
454
455 private Class<?> loadClass(final ClassLoader lastLoader, final String className) {
456 Class<?> clazz;
457 if (lastLoader != null) {
458 try {
459 clazz = lastLoader.loadClass(className);
460 if (clazz != null) {
461 return clazz;
462 }
463 } catch (final Exception ex) {
464
465 }
466 }
467 try {
468 clazz = Thread.currentThread().getContextClassLoader().loadClass(className);
469 } catch (final ClassNotFoundException e) {
470 try {
471 clazz = Class.forName(className);
472 } catch (final ClassNotFoundException e1) {
473 try {
474 clazz = getClass().getClassLoader().loadClass(className);
475 } catch (final ClassNotFoundException e2) {
476 return null;
477 }
478 }
479 }
480 return clazz;
481 }
482
483 public ThrowableProxy[] getSuppressed() {
484 if (GET_SUPPRESSED != null) {
485 try {
486 return (ThrowableProxy[]) GET_SUPPRESSED.invoke(throwable);
487 } catch (final Exception ignore) {
488 return null;
489 }
490 }
491 return null;
492 }
493
494 private void setSuppressed(final Throwable throwable) {
495 if (GET_SUPPRESSED != null && ADD_SUPPRESSED != null) {
496 try {
497 final Throwable[] array = (Throwable[]) GET_SUPPRESSED.invoke(throwable);
498 for (final Throwable t : array) {
499 ADD_SUPPRESSED.invoke(this, new ThrowableProxy(t));
500 }
501 } catch (final Exception ignore) {
502
503 }
504 }
505 }
506
507
508
509
510 class CacheEntry {
511 private final StackTracePackageElement element;
512 private final ClassLoader loader;
513
514 public CacheEntry(final StackTracePackageElement element, final ClassLoader loader) {
515 this.element = element;
516 this.loader = loader;
517 }
518 }
519
520
521
522
523 private static class PrivateSecurityManager extends SecurityManager {
524 public Class<?>[] getClasses() {
525 return getClassContext();
526 }
527 }
528 }