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.spi;
15  
16  import ch.qos.logback.core.CoreConstants;
17  
18  import java.util.List;
19  
20  /**
21   * This class computes caller data returning the result in the form
22   * of a StackTraceElement array.
23   *
24   * @author Ceki Gülcü
25   */
26  public class CallerData {
27  
28      /**
29       * When caller information is not available this constant is used for file
30       * name, method name, etc.
31       */
32      public static final String NA = "?";
33  
34      // All logger call's in log4j-over-slf4j use the Category class
35      private static final String LOG4J_CATEGORY = "org.apache.log4j.Category";
36      private static final String SLF4J_BOUNDARY = "org.slf4j.Logger";
37  
38      /**
39       * When caller information is not available this constant is used for the line
40       * number.
41       */
42      public static final int LINE_NA = -1;
43  
44      public static final String CALLER_DATA_NA = "?#?:?" + CoreConstants.LINE_SEPARATOR;
45  
46      /**
47       * This value is returned in case no caller data could be extracted.
48       */
49      public static final StackTraceElement[] EMPTY_CALLER_DATA_ARRAY = new StackTraceElement[0];
50  
51      /**
52       * Extract caller data information as an array based on a Throwable passed as
53       * parameter
54       */
55      public static StackTraceElement[] extract(Throwable t, String fqnOfInvokingClass, final int maxDepth, List<String> frameworkPackageList) {
56          if (t == null) {
57              return null;
58          }
59  
60          StackTraceElement[] steArray = t.getStackTrace();
61          StackTraceElement[] callerDataArray;
62  
63          int found = LINE_NA;
64          for (int i = 0; i < steArray.length; i++) {
65              if (isInFrameworkSpace(steArray[i].getClassName(), fqnOfInvokingClass, frameworkPackageList)) {
66                  // the caller is assumed to be the next stack frame, hence the +1.
67                  found = i + 1;
68              } else {
69                  if (found != LINE_NA) {
70                      break;
71                  }
72              }
73          }
74  
75          // we failed to extract caller data
76          if (found == LINE_NA) {
77              return EMPTY_CALLER_DATA_ARRAY;
78          }
79  
80          int availableDepth = steArray.length - found;
81          int desiredDepth = maxDepth < (availableDepth) ? maxDepth : availableDepth;
82  
83          callerDataArray = new StackTraceElement[desiredDepth];
84          for (int i = 0; i < desiredDepth; i++) {
85              callerDataArray[i] = steArray[found + i];
86          }
87          return callerDataArray;
88      }
89  
90      static boolean isInFrameworkSpace(String currentClass, String fqnOfInvokingClass, List<String> frameworkPackageList) {
91          // the check for org.apache.log4j.Category class is intended to support
92          // log4j-over-slf4j. it solves http://bugzilla.slf4j.org/show_bug.cgi?id=66
93          if (currentClass.equals(fqnOfInvokingClass) || currentClass.equals(LOG4J_CATEGORY) || currentClass.startsWith(SLF4J_BOUNDARY)
94                          || isInFrameworkSpaceList(currentClass, frameworkPackageList)) {
95              return true;
96          } else {
97              return false;
98          }
99      }
100 
101     /**
102      * Is currentClass present in the list of packages considered part of the logging framework?
103      */
104     private static boolean isInFrameworkSpaceList(String currentClass, List<String> frameworkPackageList) {
105         if (frameworkPackageList == null)
106             return false;
107 
108         for (String s : frameworkPackageList) {
109             if (currentClass.startsWith(s))
110                 return true;
111         }
112         return false;
113     }
114 
115     /**
116      * Returns a StackTraceElement where all string fields are set to {@link #NA} and line number is set to {@link #LINE_NA}.
117      *
118      * @return StackTraceElement with values set to NA constants.
119      * @since 1.0.10
120      */
121     public static StackTraceElement naInstance() {
122         return new StackTraceElement(NA, NA, NA, LINE_NA);
123     }
124 
125 }