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.spi; 015 016import ch.qos.logback.core.CoreConstants; 017 018import java.util.List; 019 020import static ch.qos.logback.core.CoreConstants.NA; 021 022/** 023 * This class computes caller data returning the result in the form of a 024 * StackTraceElement array. 025 * 026 * @author Ceki Gülcü 027 */ 028public class CallerData { 029 030 031 // All logger call's in log4j-over-slf4j use the Category class 032 private static final String LOG4J_CATEGORY = "org.apache.log4j.Category"; 033 private static final String SLF4J_BOUNDARY = "org.slf4j.Logger"; 034 035 /** 036 * When caller information is not available this constant is used for the line 037 * number. 038 */ 039 public static final int LINE_NA = -1; 040 041 public static final String CALLER_DATA_NA = "?#?:?" + CoreConstants.LINE_SEPARATOR; 042 043 /** 044 * This value is returned in case no caller data could be extracted. 045 */ 046 public static final StackTraceElement[] EMPTY_CALLER_DATA_ARRAY = new StackTraceElement[0]; 047 048 /** 049 * Extract caller data information as an array based on a Throwable passed as 050 * parameter 051 */ 052 public static StackTraceElement[] extract(Throwable t, String fqnOfInvokingClass, final int maxDepth, 053 List<String> frameworkPackageList) { 054 if (t == null) { 055 return null; 056 } 057 058 StackTraceElement[] steArray = t.getStackTrace(); 059 StackTraceElement[] callerDataArray; 060 061 int found = LINE_NA; 062 for (int i = 0; i < steArray.length; i++) { 063 if (isInFrameworkSpace(steArray[i].getClassName(), fqnOfInvokingClass, frameworkPackageList)) { 064 // the caller is assumed to be the next stack frame, hence the +1. 065 found = i + 1; 066 } else { 067 if (found != LINE_NA) { 068 break; 069 } 070 } 071 } 072 073 // we failed to extract caller data 074 if (found == LINE_NA) { 075 return EMPTY_CALLER_DATA_ARRAY; 076 } 077 078 int availableDepth = steArray.length - found; 079 int desiredDepth = maxDepth < (availableDepth) ? maxDepth : availableDepth; 080 081 callerDataArray = new StackTraceElement[desiredDepth]; 082 for (int i = 0; i < desiredDepth; i++) { 083 callerDataArray[i] = steArray[found + i]; 084 } 085 return callerDataArray; 086 } 087 088 static boolean isInFrameworkSpace(String currentClass, String fqnOfInvokingClass, 089 List<String> frameworkPackageList) { 090 // the check for org.apache.log4j.Category class is intended to support 091 // log4j-over-slf4j. it solves http://bugzilla.slf4j.org/show_bug.cgi?id=66 092 if (currentClass.equals(fqnOfInvokingClass) || currentClass.equals(LOG4J_CATEGORY) 093 || currentClass.startsWith(SLF4J_BOUNDARY) 094 || isInFrameworkSpaceList(currentClass, frameworkPackageList)) { 095 return true; 096 } else { 097 return false; 098 } 099 } 100 101 /** 102 * Is currentClass present in the list of packages considered part of the 103 * logging framework? 104 */ 105 private static boolean isInFrameworkSpaceList(String currentClass, List<String> frameworkPackageList) { 106 if (frameworkPackageList == null) 107 return false; 108 109 for (String s : frameworkPackageList) { 110 if (currentClass.startsWith(s)) 111 return true; 112 } 113 return false; 114 } 115 116 /** 117 * Returns a StackTraceElement where all string fields are set to {@link CoreConstants#NA} 118 * and line number is set to {@link #LINE_NA}. 119 * 120 * @return StackTraceElement with values set to NA constants. 121 * @since 1.0.10 122 */ 123 public static StackTraceElement naInstance() { 124 return new StackTraceElement(NA, NA, NA, LINE_NA); 125 } 126 127}