1
2
3
4
5
6
7
8
9
10
11
12
13
14 package ch.qos.logback.classic.pattern;
15
16 import java.util.ArrayList;
17 import java.util.List;
18 import java.util.Map;
19
20 import ch.qos.logback.classic.spi.ILoggingEvent;
21 import ch.qos.logback.classic.spi.IThrowableProxy;
22 import ch.qos.logback.classic.spi.StackTraceElementProxy;
23 import ch.qos.logback.classic.spi.ThrowableProxyUtil;
24 import ch.qos.logback.core.Context;
25 import ch.qos.logback.core.CoreConstants;
26 import ch.qos.logback.core.boolex.EvaluationException;
27 import ch.qos.logback.core.boolex.EventEvaluator;
28 import ch.qos.logback.core.status.ErrorStatus;
29
30
31
32
33
34
35 public class ThrowableProxyConverter extends ThrowableHandlingConverter {
36
37 protected static final int BUILDER_CAPACITY = 2048;
38
39 int lengthOption;
40 List<EventEvaluator<ILoggingEvent>> evaluatorList = null;
41 List<String> ignoredStackTraceLines = null;
42
43 int errorCount = 0;
44
45 @SuppressWarnings("unchecked")
46 public void start() {
47
48 String lengthStr = getFirstOption();
49
50 if (lengthStr == null) {
51 lengthOption = Integer.MAX_VALUE;
52 } else {
53 lengthStr = lengthStr.toLowerCase();
54 if ("full".equals(lengthStr)) {
55 lengthOption = Integer.MAX_VALUE;
56 } else if ("short".equals(lengthStr)) {
57 lengthOption = 1;
58 } else {
59 try {
60 lengthOption = Integer.parseInt(lengthStr);
61 } catch (NumberFormatException nfe) {
62 addError("Could not parse [" + lengthStr + "] as an integer");
63 lengthOption = Integer.MAX_VALUE;
64 }
65 }
66 }
67
68 final List<String> optionList = getOptionList();
69
70 if (optionList != null && optionList.size() > 1) {
71 final int optionListSize = optionList.size();
72 for (int i = 1; i < optionListSize; i++) {
73 String evaluatorOrIgnoredStackTraceLine = (String) optionList.get(i);
74 Context context = getContext();
75 Map<String, EventEvaluator<?>> evaluatorMap = (Map<String, EventEvaluator<?>>) context
76 .getObject(CoreConstants.EVALUATOR_MAP);
77 EventEvaluator<ILoggingEvent> ee = (EventEvaluator<ILoggingEvent>) evaluatorMap
78 .get(evaluatorOrIgnoredStackTraceLine);
79 if (ee != null) {
80 addEvaluator(ee);
81 } else {
82 addIgnoreStackTraceLine(evaluatorOrIgnoredStackTraceLine);
83 }
84 }
85 }
86 super.start();
87 }
88
89 private void addEvaluator(EventEvaluator<ILoggingEvent> ee) {
90 if (evaluatorList == null) {
91 evaluatorList = new ArrayList<EventEvaluator<ILoggingEvent>>();
92 }
93 evaluatorList.add(ee);
94 }
95
96 private void addIgnoreStackTraceLine(String ignoredStackTraceLine) {
97 if (ignoredStackTraceLines == null) {
98 ignoredStackTraceLines = new ArrayList<String>();
99 }
100 ignoredStackTraceLines.add(ignoredStackTraceLine);
101 }
102
103 public void stop() {
104 evaluatorList = null;
105 super.stop();
106 }
107
108 protected void extraData(StringBuilder builder, StackTraceElementProxy step) {
109
110 }
111
112 public String convert(ILoggingEvent event) {
113
114 IThrowableProxy tp = event.getThrowableProxy();
115 if (tp == null) {
116 return CoreConstants.EMPTY_STRING;
117 }
118
119
120 if (evaluatorList != null) {
121 boolean printStack = true;
122 for (int i = 0; i < evaluatorList.size(); i++) {
123 EventEvaluator<ILoggingEvent> ee = evaluatorList.get(i);
124 try {
125 if (ee.evaluate(event)) {
126 printStack = false;
127 break;
128 }
129 } catch (EvaluationException eex) {
130 errorCount++;
131 if (errorCount < CoreConstants.MAX_ERROR_COUNT) {
132 addError("Exception thrown for evaluator named [" + ee.getName() + "]", eex);
133 } else if (errorCount == CoreConstants.MAX_ERROR_COUNT) {
134 ErrorStatus errorStatus = new ErrorStatus(
135 "Exception thrown for evaluator named [" + ee.getName() + "].", this, eex);
136 errorStatus.add(new ErrorStatus("This was the last warning about this evaluator's errors."
137 + "We don't want the StatusManager to get flooded.", this));
138 addStatus(errorStatus);
139 }
140 }
141 }
142
143 if (!printStack) {
144 return CoreConstants.EMPTY_STRING;
145 }
146 }
147
148 return throwableProxyToString(tp);
149 }
150
151 protected String throwableProxyToString(IThrowableProxy tp) {
152 StringBuilder sb = new StringBuilder(BUILDER_CAPACITY);
153
154 recursiveAppend(sb, null, ThrowableProxyUtil.REGULAR_EXCEPTION_INDENT, tp);
155
156 return sb.toString();
157 }
158
159 private void recursiveAppend(StringBuilder sb, String prefix, int indent, IThrowableProxy tp) {
160 if (tp == null)
161 return;
162 subjoinFirstLine(sb, prefix, indent, tp);
163 sb.append(CoreConstants.LINE_SEPARATOR);
164 subjoinSTEPArray(sb, indent, tp);
165 IThrowableProxy[] suppressed = tp.getSuppressed();
166 if (suppressed != null) {
167 for (IThrowableProxy current : suppressed) {
168 recursiveAppend(sb, CoreConstants.SUPPRESSED, indent + ThrowableProxyUtil.SUPPRESSED_EXCEPTION_INDENT,
169 current);
170 }
171 }
172 recursiveAppend(sb, CoreConstants.CAUSED_BY, indent, tp.getCause());
173 }
174
175 private void subjoinFirstLine(StringBuilder buf, String prefix, int indent, IThrowableProxy tp) {
176 ThrowableProxyUtil.indent(buf, indent - 1);
177 if (prefix != null) {
178 buf.append(prefix);
179 }
180 subjoinExceptionMessage(buf, tp);
181 }
182
183 private void subjoinExceptionMessage(StringBuilder buf, IThrowableProxy tp) {
184 if (tp.isCyclic()) {
185 buf.append("[CIRCULAR REFERENCE: ").append(tp.getClassName()).append(": ").append(tp.getMessage())
186 .append(']');
187 } else {
188 buf.append(tp.getClassName()).append(": ").append(tp.getMessage());
189 }
190 }
191
192 protected void subjoinSTEPArray(StringBuilder buf, int indent, IThrowableProxy tp) {
193 StackTraceElementProxy[] stepArray = tp.getStackTraceElementProxyArray();
194 int commonFrames = tp.getCommonFrames();
195
196 boolean unrestrictedPrinting = lengthOption > stepArray.length;
197
198 int maxIndex = (unrestrictedPrinting) ? stepArray.length : lengthOption;
199 if (commonFrames > 0 && unrestrictedPrinting) {
200 maxIndex -= commonFrames;
201 }
202
203 int ignoredCount = 0;
204 for (int i = 0; i < maxIndex; i++) {
205 StackTraceElementProxy element = stepArray[i];
206 if (!isIgnoredStackTraceLine(element.toString())) {
207 ThrowableProxyUtil.indent(buf, indent);
208 printStackLine(buf, ignoredCount, element);
209 ignoredCount = 0;
210 buf.append(CoreConstants.LINE_SEPARATOR);
211 } else {
212 ++ignoredCount;
213 if (maxIndex < stepArray.length) {
214 ++maxIndex;
215 }
216 }
217 }
218 if (ignoredCount > 0) {
219 printIgnoredCount(buf, ignoredCount);
220 buf.append(CoreConstants.LINE_SEPARATOR);
221 }
222
223 if (commonFrames > 0 && unrestrictedPrinting) {
224 ThrowableProxyUtil.indent(buf, indent);
225 buf.append("... ").append(tp.getCommonFrames()).append(" common frames omitted")
226 .append(CoreConstants.LINE_SEPARATOR);
227 }
228 }
229
230 private void printStackLine(StringBuilder buf, int ignoredCount, StackTraceElementProxy element) {
231 buf.append(element);
232 extraData(buf, element);
233 if (ignoredCount > 0) {
234 printIgnoredCount(buf, ignoredCount);
235 }
236 }
237
238 private void printIgnoredCount(StringBuilder buf, int ignoredCount) {
239 buf.append(" [").append(ignoredCount).append(" skipped]");
240 }
241
242 private boolean isIgnoredStackTraceLine(String line) {
243 if (ignoredStackTraceLines != null) {
244 for (String ignoredStackTraceLine : ignoredStackTraceLines) {
245 if (line.contains(ignoredStackTraceLine)) {
246 return true;
247 }
248 }
249 }
250 return false;
251 }
252
253 }