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.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   * This converter outputs caller data depending on depth or depth range and marker data.
31   * 
32   * @author Ceki Gulcu
33   */
34  public class CallerDataConverter extends ClassicConverter {
35  
36      public static final String DEFAULT_CALLER_LINE_PREFIX = "Caller+";
37  
38      public static final String DEFAULT_RANGE_DELIMITER = "..";
39  
40      private int depthStart = 0;
41      private int depthEnd = 5;
42      List<EventEvaluator<ILoggingEvent>> evaluatorList = null;
43  
44      final int MAX_ERROR_COUNT = 4;
45      int errorCount = 0;
46  
47      @SuppressWarnings("unchecked")
48      public void start() {
49          String depthStr = getFirstOption();
50          if (depthStr == null) {
51              return;
52          }
53  
54          try {
55              if (isRange(depthStr)) {
56                  String[] numbers = splitRange(depthStr);
57                  if (numbers.length == 2) {
58                      depthStart = Integer.parseInt(numbers[0]);
59                      depthEnd = Integer.parseInt(numbers[1]);
60                      checkRange();
61                  } else {
62                      addError("Failed to parse depth option as range [" + depthStr + "]");
63                  }
64              } else {
65                  depthEnd = Integer.parseInt(depthStr);
66              }
67          } catch (NumberFormatException nfe) {
68              addError("Failed to parse depth option [" + depthStr + "]", nfe);
69          }
70  
71          final List<String> optionList = getOptionList();
72  
73          if (optionList != null && optionList.size() > 1) {
74              final int optionListSize = optionList.size();
75              for (int i = 1; i < optionListSize; i++) {
76                  String evaluatorStr = optionList.get(i);
77                  Context context = getContext();
78                  if (context != null) {
79                      Map<String, EventEvaluator<?>> evaluatorMap = (Map<String, EventEvaluator<?>>) context.getObject(CoreConstants.EVALUATOR_MAP);
80                      EventEvaluator<ILoggingEvent> ee = (EventEvaluator<ILoggingEvent>) evaluatorMap.get(evaluatorStr);
81                      if (ee != null) {
82                          addEvaluator(ee);
83                      }
84                  }
85              }
86          }
87      }
88  
89      private boolean isRange(String depthStr) {
90          return depthStr.contains(getDefaultRangeDelimiter());
91      }
92  
93      private String[] splitRange(String depthStr) {
94          return depthStr.split(quote(getDefaultRangeDelimiter()), 2);
95      }
96  
97      private void checkRange() {
98          if (depthStart < 0 || depthEnd < 0) {
99              addError("Invalid depthStart/depthEnd range [" + depthStart + ", " + depthEnd + "] (negative values are not allowed)");
100         } else if (depthStart >= depthEnd) {
101             addError("Invalid depthEnd range [" + depthStart + ", " + depthEnd + "] (start greater or equal to end)");
102         }
103     }
104 
105     private void addEvaluator(EventEvaluator<ILoggingEvent> ee) {
106         if (evaluatorList == null) {
107             evaluatorList = new ArrayList<EventEvaluator<ILoggingEvent>>();
108         }
109         evaluatorList.add(ee);
110     }
111 
112     public String convert(ILoggingEvent le) {
113         StringBuilder buf = new StringBuilder();
114 
115         if (evaluatorList != null) {
116             boolean printCallerData = false;
117             for (int i = 0; i < evaluatorList.size(); i++) {
118                 EventEvaluator<ILoggingEvent> ee = evaluatorList.get(i);
119                 try {
120                     if (ee.evaluate(le)) {
121                         printCallerData = true;
122                         break;
123                     }
124                 } catch (EvaluationException eex) {
125                     errorCount++;
126                     if (errorCount < MAX_ERROR_COUNT) {
127                         addError("Exception thrown for evaluator named [" + ee.getName() + "]", eex);
128                     } else if (errorCount == MAX_ERROR_COUNT) {
129                         ErrorStatus errorStatus = new ErrorStatus("Exception thrown for evaluator named [" + ee.getName() + "].", this, eex);
130                         errorStatus.add(new ErrorStatus("This was the last warning about this evaluator's errors."
131                                         + "We don't want the StatusManager to get flooded.", this));
132                         addStatus(errorStatus);
133                     }
134 
135                 }
136             }
137 
138             if (!printCallerData) {
139                 return CoreConstants.EMPTY_STRING;
140             }
141         }
142 
143         StackTraceElement[] cda = le.getCallerData();
144         if (cda != null && cda.length > depthStart) {
145             int limit = depthEnd < cda.length ? depthEnd : cda.length;
146 
147             for (int i = depthStart; i < limit; i++) {
148                 buf.append(getCallerLinePrefix());
149                 buf.append(i);
150                 buf.append("\t at ");
151                 buf.append(cda[i]);
152                 buf.append(CoreConstants.LINE_SEPARATOR);
153             }
154             return buf.toString();
155         } else {
156             return CallerData.CALLER_DATA_NA;
157         }
158     }
159 
160     protected String getCallerLinePrefix() {
161         return DEFAULT_CALLER_LINE_PREFIX;
162     }
163 
164     protected String getDefaultRangeDelimiter() {
165         return DEFAULT_RANGE_DELIMITER;
166     }
167 }