1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.logging.log4j.util;
18
19 import java.lang.reflect.Method;
20 import java.util.Stack;
21 import java.util.function.Predicate;
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48 public final class StackLocator {
49
50
51
52 static final int JDK_7u25_OFFSET;
53
54
55 private static final Method GET_CALLER_CLASS;
56
57 private static final StackLocator INSTANCE;
58
59 static {
60 Method getCallerClass;
61 int java7u25CompensationOffset = 0;
62 try {
63 final Class<?> sunReflectionClass = LoaderUtil.loadClass("sun.reflect.Reflection");
64 getCallerClass = sunReflectionClass.getDeclaredMethod("getCallerClass", int.class);
65 Object o = getCallerClass.invoke(null, 0);
66 getCallerClass.invoke(null, 0);
67 if (o == null || o != sunReflectionClass) {
68 getCallerClass = null;
69 java7u25CompensationOffset = -1;
70 } else {
71 o = getCallerClass.invoke(null, 1);
72 if (o == sunReflectionClass) {
73 System.out.println("WARNING: Java 1.7.0_25 is in use which has a broken implementation of Reflection.getCallerClass(). " +
74 " Please consider upgrading to Java 1.7.0_40 or later.");
75 java7u25CompensationOffset = 1;
76 }
77 }
78 } catch (final Exception | LinkageError e) {
79 System.out.println("WARNING: sun.reflect.Reflection.getCallerClass is not supported. This will impact performance.");
80 getCallerClass = null;
81 java7u25CompensationOffset = -1;
82 }
83
84 GET_CALLER_CLASS = getCallerClass;
85 JDK_7u25_OFFSET = java7u25CompensationOffset;
86
87 INSTANCE = new StackLocator();
88 }
89
90 public static StackLocator getInstance() {
91 return INSTANCE;
92 }
93
94 private StackLocator() {
95 }
96
97
98
99
100 @PerformanceSensitive
101 public Class<?> getCallerClass(final Class<?> sentinelClass, final Predicate<Class<?>> callerPredicate) {
102 if (sentinelClass == null) {
103 throw new IllegalArgumentException("sentinelClass cannot be null");
104 }
105 if (callerPredicate == null) {
106 throw new IllegalArgumentException("callerPredicate cannot be null");
107 }
108 boolean foundSentinel = false;
109 Class<?> clazz;
110 for (int i = 2; null != (clazz = getCallerClass(i)); i++) {
111 if (sentinelClass.equals(clazz)) {
112 foundSentinel = true;
113 } else if (foundSentinel && callerPredicate.test(clazz)) {
114 return clazz;
115 }
116 }
117 return null;
118 }
119
120
121 @PerformanceSensitive
122 public Class<?> getCallerClass(final int depth) {
123 if (depth < 0) {
124 throw new IndexOutOfBoundsException(Integer.toString(depth));
125 }
126 if (GET_CALLER_CLASS == null) {
127 return null;
128 }
129
130
131 try {
132 return (Class<?>) GET_CALLER_CLASS.invoke(null, depth + 1 + JDK_7u25_OFFSET);
133 } catch (final Exception e) {
134
135
136 return null;
137 }
138 }
139
140
141 @PerformanceSensitive
142 public Class<?> getCallerClass(final String fqcn, final String pkg) {
143 boolean next = false;
144 Class<?> clazz;
145 for (int i = 2; null != (clazz = getCallerClass(i)); i++) {
146 if (fqcn.equals(clazz.getName())) {
147 next = true;
148 continue;
149 }
150 if (next && clazz.getName().startsWith(pkg)) {
151 return clazz;
152 }
153 }
154
155 return null;
156 }
157
158
159 @PerformanceSensitive
160 public Class<?> getCallerClass(final Class<?> anchor) {
161 boolean next = false;
162 Class<?> clazz;
163 for (int i = 2; null != (clazz = getCallerClass(i)); i++) {
164 if (anchor.equals(clazz)) {
165 next = true;
166 continue;
167 }
168 if (next) {
169 return clazz;
170 }
171 }
172 return Object.class;
173 }
174
175
176 @PerformanceSensitive
177 public Stack<Class<?>> getCurrentStackTrace() {
178
179 if (PrivateSecurityManagerStackTraceUtil.isEnabled()) {
180 return PrivateSecurityManagerStackTraceUtil.getCurrentStackTrace();
181 }
182
183 final Stack<Class<?>> classes = new Stack<>();
184 Class<?> clazz;
185 for (int i = 1; null != (clazz = getCallerClass(i)); i++) {
186 classes.push(clazz);
187 }
188 return classes;
189 }
190
191 public StackTraceElement calcLocation(final String fqcnOfLogger) {
192 if (fqcnOfLogger == null) {
193 return null;
194 }
195
196 final StackTraceElement[] stackTrace = new Throwable().getStackTrace();
197 boolean found = false;
198 for (int i = 0; i < stackTrace.length; i++) {
199 final String className = stackTrace[i].getClassName();
200 if (fqcnOfLogger.equals(className)) {
201
202 found = true;
203 continue;
204 }
205 if (found && !fqcnOfLogger.equals(className)) {
206 return stackTrace[i];
207 }
208 }
209 return null;
210 }
211
212 public StackTraceElement getStackTraceElement(final int depth) {
213
214
215 final StackTraceElement[] elements = new Throwable().getStackTrace();
216 int i = 0;
217 for (final StackTraceElement element : elements) {
218 if (isValid(element)) {
219 if (i == depth) {
220 return element;
221 }
222 ++i;
223 }
224 }
225 throw new IndexOutOfBoundsException(Integer.toString(depth));
226 }
227
228 private boolean isValid(final StackTraceElement element) {
229
230 if (element.isNativeMethod()) {
231 return false;
232 }
233 final String cn = element.getClassName();
234
235 if (cn.startsWith("sun.reflect.")) {
236 return false;
237 }
238 final String mn = element.getMethodName();
239
240
241
242
243 if (cn.startsWith("java.lang.reflect.") && (mn.equals("invoke") || mn.equals("newInstance"))) {
244 return false;
245 }
246
247 if (cn.startsWith("jdk.internal.reflect.")) {
248 return false;
249 }
250
251 if (cn.equals("java.lang.Class") && mn.equals("newInstance")) {
252 return false;
253 }
254
255 if (cn.equals("java.lang.invoke.MethodHandle") && mn.startsWith("invoke")) {
256 return false;
257 }
258
259 return true;
260 }
261 }