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 static java.util.regex.Pattern.quote;
21 import ch.qos.logback.classic.spi.CallerData;
22 import ch.qos.logback.classic.spi.ILoggingEvent;
23 import ch.qos.logback.core.Context;
24 import ch.qos.logback.core.CoreConstants;
25 import ch.qos.logback.core.boolex.EvaluationException;
26 import ch.qos.logback.core.boolex.EventEvaluator;
27 import ch.qos.logback.core.status.ErrorStatus;
28
29
30
31
32
33
34
35 public class CallerDataConverter extends ClassicConverter {
36
37 public static final String DEFAULT_CALLER_LINE_PREFIX = "Caller+";
38
39 public static final String DEFAULT_RANGE_DELIMITER = "..";
40
41 private int depthStart = 0;
42 private int depthEnd = 5;
43 List<EventEvaluator<ILoggingEvent>> evaluatorList = null;
44
45 final int MAX_ERROR_COUNT = 4;
46 int errorCount = 0;
47
48 @SuppressWarnings("unchecked")
49 public void start() {
50 String depthStr = getFirstOption();
51 if (depthStr == null) {
52 return;
53 }
54
55 try {
56 if (isRange(depthStr)) {
57 String[] numbers = splitRange(depthStr);
58 if (numbers.length == 2) {
59 depthStart = Integer.parseInt(numbers[0]);
60 depthEnd = Integer.parseInt(numbers[1]);
61 checkRange();
62 } else {
63 addError("Failed to parse depth option as range [" + depthStr + "]");
64 }
65 } else {
66 depthEnd = Integer.parseInt(depthStr);
67 }
68 } catch (NumberFormatException nfe) {
69 addError("Failed to parse depth option [" + depthStr + "]", nfe);
70 }
71
72 final List<String> optionList = getOptionList();
73
74 if (optionList != null && optionList.size() > 1) {
75 final int optionListSize = optionList.size();
76 for (int i = 1; i < optionListSize; i++) {
77 String evaluatorStr = optionList.get(i);
78 Context context = getContext();
79 if (context != null) {
80 Map<String, EventEvaluator<?>> evaluatorMap = (Map<String, EventEvaluator<?>>) context
81 .getObject(CoreConstants.EVALUATOR_MAP);
82 EventEvaluator<ILoggingEvent> ee = (EventEvaluator<ILoggingEvent>) evaluatorMap.get(evaluatorStr);
83 if (ee != null) {
84 addEvaluator(ee);
85 }
86 }
87 }
88 }
89 }
90
91 private boolean isRange(String depthStr) {
92 return depthStr.contains(getDefaultRangeDelimiter());
93 }
94
95 private String[] splitRange(String depthStr) {
96 return depthStr.split(quote(getDefaultRangeDelimiter()), 2);
97 }
98
99 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 }