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