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
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 public final class StackLocator {
48
49 private static PrivateSecurityManager SECURITY_MANAGER;
50
51
52
53 static final int JDK_7u25_OFFSET;
54
55
56 private static final boolean SUN_REFLECTION_SUPPORTED;
57 private static final Method GET_CALLER_CLASS;
58
59 private static final StackLocator INSTANCE;
60
61 static {
62 Method getCallerClass;
63 int java7u25CompensationOffset = 0;
64 try {
65 final Class<?> sunReflectionClass = LoaderUtil.loadClass("sun.reflect.Reflection");
66 getCallerClass = sunReflectionClass.getDeclaredMethod("getCallerClass", int.class);
67 Object o = getCallerClass.invoke(null, 0);
68 final Object test1 = getCallerClass.invoke(null, 0);
69 if (o == null || o != sunReflectionClass) {
70 getCallerClass = null;
71 java7u25CompensationOffset = -1;
72 } else {
73 o = getCallerClass.invoke(null, 1);
74 if (o == sunReflectionClass) {
75 System.out.println("WARNING: Java 1.7.0_25 is in use which has a broken implementation of Reflection.getCallerClass(). " +
76 " Please consider upgrading to Java 1.7.0_40 or later.");
77 java7u25CompensationOffset = 1;
78 }
79 }
80 } catch (final Exception | LinkageError e) {
81 System.out.println("WARNING: sun.reflect.Reflection.getCallerClass is not supported. This will impact performance.");
82 getCallerClass = null;
83 java7u25CompensationOffset = -1;
84 }
85
86 SUN_REFLECTION_SUPPORTED = getCallerClass != null;
87 GET_CALLER_CLASS = getCallerClass;
88 JDK_7u25_OFFSET = java7u25CompensationOffset;
89
90 INSTANCE = new StackLocator();
91 }
92
93 public static StackLocator getInstance() {
94 return INSTANCE;
95 }
96
97 private StackLocator() {
98 }
99
100
101
102
103
104 @PerformanceSensitive
105 public Class<?> getCallerClass(final int depth) {
106 if (depth < 0) {
107 throw new IndexOutOfBoundsException(Integer.toString(depth));
108 }
109
110
111 try {
112 return (Class<?>) GET_CALLER_CLASS.invoke(null, depth + 1 + JDK_7u25_OFFSET);
113 } catch (final Exception e) {
114
115
116 return null;
117 }
118 }
119
120
121 @PerformanceSensitive
122 public Class<?> getCallerClass(final String fqcn, final String pkg) {
123 boolean next = false;
124 Class<?> clazz;
125 for (int i = 2; null != (clazz = getCallerClass(i)); i++) {
126 if (fqcn.equals(clazz.getName())) {
127 next = true;
128 continue;
129 }
130 if (next && clazz.getName().startsWith(pkg)) {
131 return clazz;
132 }
133 }
134
135 return null;
136 }
137
138
139 @PerformanceSensitive
140 public Class<?> getCallerClass(final Class<?> anchor) {
141 boolean next = false;
142 Class<?> clazz;
143 for (int i = 2; null != (clazz = getCallerClass(i)); i++) {
144 if (anchor.equals(clazz)) {
145 next = true;
146 continue;
147 }
148 if (next) {
149 return clazz;
150 }
151 }
152 return Object.class;
153 }
154
155
156 @PerformanceSensitive
157 public Stack<Class<?>> getCurrentStackTrace() {
158
159 if (getSecurityManager() != null) {
160 final Class<?>[] array = getSecurityManager().getClassContext();
161 final Stack<Class<?>> classes = new Stack<>();
162 classes.ensureCapacity(array.length);
163 for (final Class<?> clazz : array) {
164 classes.push(clazz);
165 }
166 return classes;
167 }
168
169 final Stack<Class<?>> classes = new Stack<>();
170 Class<?> clazz;
171 for (int i = 1; null != (clazz = getCallerClass(i)); i++) {
172 classes.push(clazz);
173 }
174 return classes;
175 }
176
177 public StackTraceElement calcLocation(final String fqcnOfLogger) {
178 if (fqcnOfLogger == null) {
179 return null;
180 }
181
182 final StackTraceElement[] stackTrace = new Throwable().getStackTrace();
183 StackTraceElement last = null;
184 for (int i = stackTrace.length - 1; i > 0; i--) {
185 final String className = stackTrace[i].getClassName();
186 if (fqcnOfLogger.equals(className)) {
187 return last;
188 }
189 last = stackTrace[i];
190 }
191 return null;
192 }
193
194 public StackTraceElement getStackTraceElement(final int depth) {
195
196
197 final StackTraceElement[] elements = new Throwable().getStackTrace();
198 int i = 0;
199 for (final StackTraceElement element : elements) {
200 if (isValid(element)) {
201 if (i == depth) {
202 return element;
203 }
204 ++i;
205 }
206 }
207 throw new IndexOutOfBoundsException(Integer.toString(depth));
208 }
209
210 private boolean isValid(final StackTraceElement element) {
211
212 if (element.isNativeMethod()) {
213 return false;
214 }
215 final String cn = element.getClassName();
216
217 if (cn.startsWith("sun.reflect.")) {
218 return false;
219 }
220 final String mn = element.getMethodName();
221
222
223
224
225 if (cn.startsWith("java.lang.reflect.") && (mn.equals("invoke") || mn.equals("newInstance"))) {
226 return false;
227 }
228
229 if (cn.startsWith("jdk.internal.reflect.")) {
230 return false;
231 }
232
233 if (cn.equals("java.lang.Class") && mn.equals("newInstance")) {
234 return false;
235 }
236
237 if (cn.equals("java.lang.invoke.MethodHandle") && mn.startsWith("invoke")) {
238 return false;
239 }
240
241 return true;
242 }
243
244 protected PrivateSecurityManager getSecurityManager() {
245 return SECURITY_MANAGER;
246 }
247
248 private static final class PrivateSecurityManager extends SecurityManager {
249
250 @Override
251 protected Class<?>[] getClassContext() {
252 return super.getClassContext();
253 }
254
255 protected Class<?> getCallerClass(final String fqcn, final String pkg) {
256 boolean next = false;
257 for (final Class<?> clazz : getClassContext()) {
258 if (fqcn.equals(clazz.getName())) {
259 next = true;
260 continue;
261 }
262 if (next && clazz.getName().startsWith(pkg)) {
263 return clazz;
264 }
265 }
266
267 return null;
268 }
269
270 protected Class<?> getCallerClass(final Class<?> anchor) {
271 boolean next = false;
272 for (final Class<?> clazz : getClassContext()) {
273 if (anchor.equals(clazz)) {
274 next = true;
275 continue;
276 }
277 if (next) {
278 return clazz;
279 }
280 }
281 return Object.class;
282 }
283 }
284 }