001/** 002 * Logback: the reliable, generic, fast and flexible logging framework. 003 * Copyright (C) 1999-2015, QOS.ch. All rights reserved. 004 * 005 * This program and the accompanying materials are dual-licensed under 006 * either the terms of the Eclipse Public License v1.0 as published by 007 * the Eclipse Foundation 008 * 009 * or (per the licensee's choosing) 010 * 011 * under the terms of the GNU Lesser General Public License version 2.1 012 * as published by the Free Software Foundation. 013 */ 014package ch.qos.logback.classic.pattern; 015 016import java.util.ArrayList; 017import java.util.List; 018import java.util.Map; 019 020import ch.qos.logback.classic.spi.ILoggingEvent; 021import ch.qos.logback.classic.spi.IThrowableProxy; 022import ch.qos.logback.classic.spi.StackTraceElementProxy; 023import ch.qos.logback.classic.spi.ThrowableProxyUtil; 024import ch.qos.logback.core.Context; 025import ch.qos.logback.core.CoreConstants; 026import ch.qos.logback.core.boolex.EvaluationException; 027import ch.qos.logback.core.boolex.EventEvaluator; 028import ch.qos.logback.core.status.ErrorStatus; 029 030/** 031 * Add a stack trace in case the event contains a Throwable. 032 * 033 * @author Ceki Gülcü 034 */ 035public class ThrowableProxyConverter extends ThrowableHandlingConverter { 036 037 protected static final int BUILDER_CAPACITY = 2048; 038 039 int lengthOption; 040 List<EventEvaluator<ILoggingEvent>> evaluatorList = null; 041 List<String> ignoredStackTraceLines = null; 042 043 int errorCount = 0; 044 045 @SuppressWarnings("unchecked") 046 public void start() { 047 048 String lengthStr = getFirstOption(); 049 050 if (lengthStr == null) { 051 lengthOption = Integer.MAX_VALUE; 052 } else { 053 lengthStr = lengthStr.toLowerCase(); 054 if ("full".equals(lengthStr)) { 055 lengthOption = Integer.MAX_VALUE; 056 } else if ("short".equals(lengthStr)) { 057 lengthOption = 1; 058 } else { 059 try { 060 lengthOption = Integer.parseInt(lengthStr); 061 } catch (NumberFormatException nfe) { 062 addError("Could not parse [" + lengthStr + "] as an integer"); 063 lengthOption = Integer.MAX_VALUE; 064 } 065 } 066 } 067 068 final List<String> optionList = getOptionList(); 069 070 if (optionList != null && optionList.size() > 1) { 071 final int optionListSize = optionList.size(); 072 for (int i = 1; i < optionListSize; i++) { 073 String evaluatorOrIgnoredStackTraceLine = (String) optionList.get(i); 074 Context context = getContext(); 075 Map<String, EventEvaluator<?>> evaluatorMap = (Map<String, EventEvaluator<?>>) context 076 .getObject(CoreConstants.EVALUATOR_MAP); 077 EventEvaluator<ILoggingEvent> ee = (EventEvaluator<ILoggingEvent>) evaluatorMap 078 .get(evaluatorOrIgnoredStackTraceLine); 079 if (ee != null) { 080 addEvaluator(ee); 081 } else { 082 addIgnoreStackTraceLine(evaluatorOrIgnoredStackTraceLine); 083 } 084 } 085 } 086 super.start(); 087 } 088 089 private void addEvaluator(EventEvaluator<ILoggingEvent> ee) { 090 if (evaluatorList == null) { 091 evaluatorList = new ArrayList<EventEvaluator<ILoggingEvent>>(); 092 } 093 evaluatorList.add(ee); 094 } 095 096 private void addIgnoreStackTraceLine(String ignoredStackTraceLine) { 097 if (ignoredStackTraceLines == null) { 098 ignoredStackTraceLines = new ArrayList<String>(); 099 } 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 // nop 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 // an evaluator match will cause stack printing to be skipped 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); // allow other data to be added 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}