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 static java.util.regex.Pattern.quote; 021import ch.qos.logback.classic.spi.CallerData; 022import ch.qos.logback.classic.spi.ILoggingEvent; 023import ch.qos.logback.core.Context; 024import ch.qos.logback.core.CoreConstants; 025import ch.qos.logback.core.boolex.EvaluationException; 026import ch.qos.logback.core.boolex.EventEvaluator; 027import ch.qos.logback.core.status.ErrorStatus; 028 029/** 030 * This converter outputs caller data depending on depth or depth range and 031 * marker data. 032 * 033 * @author Ceki Gulcu 034 */ 035public class CallerDataConverter extends ClassicConverter { 036 037 public static final String DEFAULT_CALLER_LINE_PREFIX = "Caller+"; 038 039 public static final String DEFAULT_RANGE_DELIMITER = ".."; 040 041 private int depthStart = 0; 042 private int depthEnd = 5; 043 List<EventEvaluator<ILoggingEvent>> evaluatorList = null; 044 045 final int MAX_ERROR_COUNT = 4; 046 int errorCount = 0; 047 048 @SuppressWarnings("unchecked") 049 public void start() { 050 String depthStr = getFirstOption(); 051 if (depthStr == null) { 052 return; 053 } 054 055 try { 056 if (isRange(depthStr)) { 057 String[] numbers = splitRange(depthStr); 058 if (numbers.length == 2) { 059 depthStart = Integer.parseInt(numbers[0]); 060 depthEnd = Integer.parseInt(numbers[1]); 061 checkRange(); 062 } else { 063 addError("Failed to parse depth option as range [" + depthStr + "]"); 064 } 065 } else { 066 depthEnd = Integer.parseInt(depthStr); 067 } 068 } catch (NumberFormatException nfe) { 069 addError("Failed to parse depth option [" + depthStr + "]", nfe); 070 } 071 072 final List<String> optionList = getOptionList(); 073 074 if (optionList != null && optionList.size() > 1) { 075 final int optionListSize = optionList.size(); 076 for (int i = 1; i < optionListSize; i++) { 077 String evaluatorStr = optionList.get(i); 078 Context context = getContext(); 079 if (context != null) { 080 Map<String, EventEvaluator<?>> evaluatorMap = (Map<String, EventEvaluator<?>>) context 081 .getObject(CoreConstants.EVALUATOR_MAP); 082 EventEvaluator<ILoggingEvent> ee = (EventEvaluator<ILoggingEvent>) evaluatorMap.get(evaluatorStr); 083 if (ee != null) { 084 addEvaluator(ee); 085 } 086 } 087 } 088 } 089 } 090 091 private boolean isRange(String depthStr) { 092 return depthStr.contains(getDefaultRangeDelimiter()); 093 } 094 095 private String[] splitRange(String depthStr) { 096 return depthStr.split(quote(getDefaultRangeDelimiter()), 2); 097 } 098 099 private void checkRange() { 100 if (depthStart < 0 || depthEnd < 0) { 101 addError("Invalid depthStart/depthEnd range [" + depthStart + ", " + depthEnd 102 + "] (negative values are not allowed)"); 103 } else if (depthStart >= depthEnd) { 104 addError("Invalid depthEnd range [" + depthStart + ", " + depthEnd + "] (start greater or equal to end)"); 105 } 106 } 107 108 private void addEvaluator(EventEvaluator<ILoggingEvent> ee) { 109 if (evaluatorList == null) { 110 evaluatorList = new ArrayList<EventEvaluator<ILoggingEvent>>(); 111 } 112 evaluatorList.add(ee); 113 } 114 115 public String convert(ILoggingEvent le) { 116 StringBuilder buf = new StringBuilder(); 117 118 if (evaluatorList != null) { 119 boolean printCallerData = false; 120 for (int i = 0; i < evaluatorList.size(); i++) { 121 EventEvaluator<ILoggingEvent> ee = evaluatorList.get(i); 122 try { 123 if (ee.evaluate(le)) { 124 printCallerData = true; 125 break; 126 } 127 } catch (EvaluationException eex) { 128 errorCount++; 129 if (errorCount < MAX_ERROR_COUNT) { 130 addError("Exception thrown for evaluator named [" + ee.getName() + "]", eex); 131 } else if (errorCount == MAX_ERROR_COUNT) { 132 ErrorStatus errorStatus = new ErrorStatus( 133 "Exception thrown for evaluator named [" + ee.getName() + "].", this, eex); 134 errorStatus.add(new ErrorStatus("This was the last warning about this evaluator's errors." 135 + "We don't want the StatusManager to get flooded.", this)); 136 addStatus(errorStatus); 137 } 138 139 } 140 } 141 142 if (!printCallerData) { 143 return CoreConstants.EMPTY_STRING; 144 } 145 } 146 147 StackTraceElement[] cda = le.getCallerData(); 148 if (cda != null && cda.length > depthStart) { 149 int limit = depthEnd < cda.length ? depthEnd : cda.length; 150 151 for (int i = depthStart; i < limit; i++) { 152 buf.append(getCallerLinePrefix()); 153 buf.append(i); 154 buf.append("\t at "); 155 buf.append(cda[i]); 156 buf.append(CoreConstants.LINE_SEPARATOR); 157 } 158 return buf.toString(); 159 } else { 160 return CallerData.CALLER_DATA_NA; 161 } 162 } 163 164 protected String getCallerLinePrefix() { 165 return DEFAULT_CALLER_LINE_PREFIX; 166 } 167 168 protected String getDefaultRangeDelimiter() { 169 return DEFAULT_RANGE_DELIMITER; 170 } 171}