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,
137      *             IThrowableProxy tp) instead.
138      */
139     public static void subjoinSTEPArray(StringBuilder sb, IThrowableProxy tp) {
140         // not called anymore - but it is public
141         subjoinSTEPArray(sb, REGULAR_EXCEPTION_INDENT, tp);
142     }
143 
144     /**
145      * @param sb          The StringBuilder the STEPs are appended to.
146      * @param indentLevel indentation level used for the STEPs, usually
147      *                    REGULAR_EXCEPTION_INDENT.
148      * @param tp          the IThrowableProxy containing the STEPs.
149      */
150     public static void subjoinSTEPArray(StringBuilder sb, int indentLevel, IThrowableProxy tp) {
151         StackTraceElementProxy[] stepArray = tp.getStackTraceElementProxyArray();
152         int commonFrames = tp.getCommonFrames();
153 
154         for (int i = 0; i < stepArray.length - commonFrames; i++) {
155             StackTraceElementProxy step = stepArray[i];
156             indent(sb, indentLevel);
157             subjoinSTEP(sb, step);
158             sb.append(CoreConstants.LINE_SEPARATOR);
159         }
160 
161         if (commonFrames > 0) {
162             indent(sb, indentLevel);
163             sb.append("... ").append(commonFrames).append(" common frames omitted")
164                     .append(CoreConstants.LINE_SEPARATOR);
165         }
166 
167     }
168 
169     public static void subjoinFirstLine(StringBuilder buf, IThrowableProxy tp) {
170         int commonFrames = tp.getCommonFrames();
171         if (commonFrames > 0) {
172             buf.append(CoreConstants.CAUSED_BY);
173         }
174         subjoinExceptionMessage(buf, tp);
175     }
176 
177     public static void subjoinFirstLineRootCauseFirst(StringBuilder buf, IThrowableProxy tp) {
178         if (tp.getCause() != null) {
179             buf.append(CoreConstants.WRAPPED_BY);
180         }
181         subjoinExceptionMessage(buf, tp);
182     }
183 
184     private static void subjoinExceptionMessage(StringBuilder buf, IThrowableProxy tp) {
185         if (tp.isCyclic()) {
186             buf.append("[CIRCULAR REFERENCE: ").append(tp.getClassName()).append(": ").append(tp.getMessage())
187                     .append(']');
188         } else {
189             buf.append(tp.getClassName()).append(": ").append(tp.getMessage());
190         }
191     }
192 }