View Javadoc
1   /**
2    * Logback: the reliable, generic, fast and flexible logging framework.
3    * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
4    *
5    * This program and the accompanying materials are dual-licensed under
6    * either the terms of the Eclipse Public License v1.0 as published by
7    * the Eclipse Foundation
8    *
9    *   or (per the licensee's choosing)
10   *
11   * under the terms of the GNU Lesser General Public License version 2.1
12   * as published by the Free Software Foundation.
13   */
14  package ch.qos.logback.classic.spi;
15  
16  import ch.qos.logback.core.CoreConstants;
17  
18  /**
19   * Convert a throwable into an array of ThrowableDataPoint objects.
20   *
21   *
22   * @author Ceki Gülcü
23   */
24  public class ThrowableProxyUtil {
25  
26      public static final int REGULAR_EXCEPTION_INDENT = 1;
27      public static final int SUPPRESSED_EXCEPTION_INDENT = 1;
28      private static final int BUILDER_CAPACITY = 2048;
29  
30      public static void build(ThrowableProxy nestedTP, Throwable nestedThrowable, ThrowableProxy parentTP) {
31  
32          StackTraceElement[] nestedSTE = nestedThrowable.getStackTrace();
33  
34          int commonFramesCount = -1;
35          if (parentTP != null) {
36              commonFramesCount = findNumberOfCommonFrames(nestedSTE, parentTP.getStackTraceElementProxyArray());
37          }
38  
39          nestedTP.commonFrames = commonFramesCount;
40          nestedTP.stackTraceElementProxyArray = steArrayToStepArray(nestedSTE);
41      }
42  
43      static StackTraceElementProxy[] steArrayToStepArray(StackTraceElement[] stea) {
44          if (stea == null) {
45              return new StackTraceElementProxy[0];
46          }
47          StackTraceElementProxy[] stepa = new StackTraceElementProxy[stea.length];
48          for (int i = 0; i < stepa.length; i++) {
49              stepa[i] = new StackTraceElementProxy(stea[i]);
50          }
51          return stepa;
52      }
53  
54      static int findNumberOfCommonFrames(StackTraceElement[] steArray, StackTraceElementProxy[] parentSTEPArray) {
55          if (parentSTEPArray == null || steArray == null) {
56              return 0;
57          }
58  
59          int steIndex = steArray.length - 1;
60          int parentIndex = parentSTEPArray.length - 1;
61          int count = 0;
62          while (steIndex >= 0 && parentIndex >= 0) {
63              StackTraceElement ste = steArray[steIndex];
64              StackTraceElement otherSte = parentSTEPArray[parentIndex].ste;
65              if (ste.equals(otherSte)) {
66                  count++;
67              } else {
68                  break;
69              }
70              steIndex--;
71              parentIndex--;
72          }
73          return count;
74      }
75  
76      public static String asString(IThrowableProxy tp) {
77          StringBuilder sb = new StringBuilder(BUILDER_CAPACITY);
78  
79          recursiveAppend(sb, null, REGULAR_EXCEPTION_INDENT, tp);
80  
81          return sb.toString();
82      }
83  
84      private static void recursiveAppend(StringBuilder sb, String prefix, int indent, IThrowableProxy tp) {
85          if (tp == null)
86              return;
87          subjoinFirstLine(sb, prefix, indent, tp);
88          sb.append(CoreConstants.LINE_SEPARATOR);
89          subjoinSTEPArray(sb, indent, tp);
90          IThrowableProxy[] suppressed = tp.getSuppressed();
91          if (suppressed != null) {
92              for (IThrowableProxy current : suppressed) {
93                  recursiveAppend(sb, CoreConstants.SUPPRESSED, indent + SUPPRESSED_EXCEPTION_INDENT, current);
94              }
95          }
96          recursiveAppend(sb, CoreConstants.CAUSED_BY, indent, tp.getCause());
97      }
98  
99      public static void indent(StringBuilder buf, int indent) {
100         for (int j = 0; j < indent; j++) {
101             buf.append(CoreConstants.TAB);
102         }
103     }
104 
105     private static void subjoinFirstLine(StringBuilder buf, String prefix, int indent, IThrowableProxy tp) {
106         indent(buf, indent - 1);
107         if (prefix != null) {
108             buf.append(prefix);
109         }
110         subjoinExceptionMessage(buf, tp);
111     }
112 
113     public static void subjoinPackagingData(StringBuilder builder, StackTraceElementProxy step) {
114         if (step != null) {
115             ClassPackagingData cpd = step.getClassPackagingData();
116             if (cpd != null) {
117                 if (!cpd.isExact()) {
118                     builder.append(" ~[");
119                 } else {
120                     builder.append(" [");
121                 }
122 
123                 builder.append(cpd.getCodeLocation()).append(':').append(cpd.getVersion()).append(']');
124             }
125         }
126     }
127 
128     public static void subjoinSTEP(StringBuilder sb, StackTraceElementProxy step) {
129         sb.append(step.toString());
130         subjoinPackagingData(sb, step);
131     }
132 
133     /**
134      * @param sb The StringBuilder the STEPs are appended to.
135      * @param tp the IThrowableProxy containing the STEPs.
136      * @deprecated Use subjoinSTEPArray(StringBuilder sb, int indentLevel, IThrowableProxy tp) instead.
137      */
138     public static void subjoinSTEPArray(StringBuilder sb, IThrowableProxy tp) {
139         // not called anymore - but it is public
140         subjoinSTEPArray(sb, REGULAR_EXCEPTION_INDENT, tp);
141     }
142 
143     /**
144      * @param sb The StringBuilder the STEPs are appended to.
145      * @param indentLevel indentation level used for the STEPs, usually REGULAR_EXCEPTION_INDENT.
146      * @param tp the IThrowableProxy containing the STEPs.
147      */
148     public static void subjoinSTEPArray(StringBuilder sb, int indentLevel, IThrowableProxy tp) {
149         StackTraceElementProxy[] stepArray = tp.getStackTraceElementProxyArray();
150         int commonFrames = tp.getCommonFrames();
151 
152         for (int i = 0; i < stepArray.length - commonFrames; i++) {
153             StackTraceElementProxy step = stepArray[i];
154             indent(sb, indentLevel);
155             subjoinSTEP(sb, step);
156             sb.append(CoreConstants.LINE_SEPARATOR);
157         }
158 
159         if (commonFrames > 0) {
160             indent(sb, indentLevel);
161             sb.append("... ").append(commonFrames).append(" common frames omitted").append(CoreConstants.LINE_SEPARATOR);
162         }
163 
164     }
165 
166     public static void subjoinFirstLine(StringBuilder buf, IThrowableProxy tp) {
167         int commonFrames = tp.getCommonFrames();
168         if (commonFrames > 0) {
169             buf.append(CoreConstants.CAUSED_BY);
170         }
171         subjoinExceptionMessage(buf, tp);
172     }
173 
174     public static void subjoinFirstLineRootCauseFirst(StringBuilder buf, IThrowableProxy tp) {
175         if (tp.getCause() != null) {
176             buf.append(CoreConstants.WRAPPED_BY);
177         }
178         subjoinExceptionMessage(buf, tp);
179     }
180 
181     private static void subjoinExceptionMessage(StringBuilder buf, IThrowableProxy tp) {
182         buf.append(tp.getClassName()).append(": ").append(tp.getMessage());
183     }
184 }