18
18
19
19
import java .util .ArrayDeque ;
20
20
import java .util .Collections ;
21
+ import java .util .Deque ;
22
+ import java .util .HashMap ;
21
23
import java .util .HashSet ;
22
24
import java .util .List ;
23
25
import java .util .Map ;
24
- import java .util .Objects ;
25
26
import java .util .Queue ;
26
27
import java .util .Set ;
27
- import java .util .stream .Collectors ;
28
- import java .util .stream .Stream ;
29
28
import org .apache .logging .log4j .util .LoaderUtil ;
30
29
import org .apache .logging .log4j .util .StackLocatorUtil ;
31
30
@@ -74,7 +73,7 @@ void renderStackTraceElement(
74
73
context .classResourceInfoByName .get (stackTraceElement .getClassName ());
75
74
if (classResourceInfo != null ) {
76
75
buffer .append (' ' );
77
- buffer . append ( classResourceInfo );
76
+ classResourceInfo . render ( buffer );
78
77
}
79
78
buffer .append (lineSeparator );
80
79
}
@@ -102,15 +101,15 @@ private static Map<String, ClassResourceInfo> createClassResourceInfoByName(
102
101
final Throwable rootThrowable , final Map <Throwable , Metadata > metadataByThrowable ) {
103
102
104
103
// Stack trace elements of a `Throwable` only contain the class name.
105
- // But we need the associated `Class<?> ` to extract its resource information, i.e., JAR file and version.
104
+ // But we need the associated `Class` to extract its resource information, i.e., JAR file and version.
106
105
// We are capturing the current stack to find suitable class loaders.
107
- // We will use this as a bootstrap to go from a class name in a stack trace to a `Class<?> `.
108
- final Map < String , ClassResourceInfo > classResourceInfoByName =
109
- StackLocatorUtil . getCurrentStackTrace (). stream ()
110
- . collect ( Collectors . toMap (
111
- Class :: getName ,
112
- clazz -> new ClassResourceInfo ( clazz , true ),
113
- ( classResourceInfo1 , classResourceInfo2 ) -> classResourceInfo1 ) );
106
+ // We will use this as a bootstrap to go from a class name in a stack trace to a `Class`.
107
+ final Deque < Class <?>> executionStackTrace = StackLocatorUtil . getCurrentStackTrace ();
108
+
109
+ // Mapping a class name to a `ClassResourceInfo` is an expensive operation.
110
+ // Next to `ClassResourceInfo` allocation, it requires extraction of the associated ` Class`.
111
+ // We will use this lookup table to speed things up.
112
+ final Map < String , ClassResourceInfo > classResourceInfoByName = new HashMap <>( );
114
113
115
114
// Walk over the causal chain
116
115
final Set <Throwable > visitedThrowables = new HashSet <>();
@@ -130,64 +129,78 @@ private static Map<String, ClassResourceInfo> createClassResourceInfoByName(
130
129
continue ;
131
130
}
132
131
132
+ Class <?> executionStackTraceElementClass =
133
+ executionStackTrace .isEmpty () ? null : executionStackTrace .peekLast ();
133
134
ClassLoader lastLoader = null ;
134
135
final StackTraceElement [] stackTraceElements = throwable .getStackTrace ();
135
136
for (int throwableStackIndex = metadata .stackLength - 1 ;
136
137
throwableStackIndex >= 0 ;
137
138
--throwableStackIndex ) {
138
139
139
- // Skip if the current class name is either known, or already visited and is unknown
140
- final StackTraceElement stackTraceElement = stackTraceElements [throwableStackIndex ];
141
- final String stackTraceElementClassName = stackTraceElement .getClassName ();
142
- ClassResourceInfo classResourceInfo = classResourceInfoByName .get (stackTraceElementClassName );
140
+ // Get the exception's stack trace element
141
+ final StackTraceElement throwableStackTraceElement = stackTraceElements [throwableStackIndex ];
142
+ final String throwableStackTraceElementClassName = throwableStackTraceElement .getClassName ();
143
+
144
+ // Skip if the current class name is already registered
145
+ ClassResourceInfo classResourceInfo =
146
+ classResourceInfoByName .get (throwableStackTraceElementClassName );
143
147
if (classResourceInfo != null ) {
144
148
if (classResourceInfo .clazz != null ) {
145
149
lastLoader = classResourceInfo .clazz .getClassLoader ();
146
150
}
147
- continue ;
148
151
}
149
152
150
- // Try to determine the stack trace element class, and register the result to the lookup table
151
- final Class <?> stackTraceElementClass = loadClass (lastLoader , stackTraceElementClassName );
152
- classResourceInfo = stackTraceElementClass != null
153
- ? new ClassResourceInfo (stackTraceElementClass , false )
154
- : ClassResourceInfo .UNKNOWN ;
155
- classResourceInfoByName .put (stackTraceElementClassName , classResourceInfo );
153
+ // See if we get a match from the execution stack trace
154
+ else if (executionStackTraceElementClass != null
155
+ && throwableStackTraceElementClassName .equals (executionStackTraceElementClass .getName ())) {
156
+ classResourceInfo = new ClassResourceInfo (executionStackTraceElementClass , true );
157
+ classResourceInfoByName .put (throwableStackTraceElementClassName , classResourceInfo );
158
+ lastLoader = classResourceInfo .clazz .getClassLoader ();
159
+ executionStackTrace .pollLast ();
160
+ executionStackTraceElementClass = executionStackTrace .peekLast ();
161
+ }
162
+
163
+ // We don't know this class name, try to load it using the last found loader
164
+ else {
165
+ final Class <?> stackTraceElementClass =
166
+ loadClass (lastLoader , throwableStackTraceElementClassName );
167
+ classResourceInfo = stackTraceElementClass != null
168
+ ? new ClassResourceInfo (stackTraceElementClass , false )
169
+ : ClassResourceInfo .UNKNOWN ;
170
+ classResourceInfoByName .put (throwableStackTraceElementClassName , classResourceInfo );
171
+ }
156
172
}
157
173
}
158
174
return classResourceInfoByName ;
159
175
}
160
176
161
- @ FunctionalInterface
162
- private interface ThrowingSupplier <V > {
163
-
164
- V supply () throws Exception ;
165
- }
166
-
167
177
private static Class <?> loadClass (final ClassLoader loader , final String className ) {
168
- return Stream .<ThrowingSupplier <Class <?>>>of (
169
- // 1. Try the passed class loader
170
- () -> loader != null ? loader .loadClass (className ) : null ,
171
- // 2. Try the `LoaderUtil` magic
172
- () -> LoaderUtil .loadClass (className ),
173
- // 3. Try the current class loader
174
- () -> ThrowableExtendedStackTraceRenderer .class
175
- .getClassLoader ()
176
- .loadClass (className ))
177
- .map (provider -> {
178
- try {
179
- final Class <?> clazz = provider .supply ();
180
- if (clazz != null ) {
181
- return clazz ;
182
- }
183
- } catch (final Exception ignored ) {
184
- // Do nothing
185
- }
186
- return null ;
187
- })
188
- .filter (Objects ::nonNull )
189
- .findFirst ()
190
- .orElse (null );
178
+ for (final ClassLoadingStrategy strategy : CLASS_LOADING_STRATEGIES ) {
179
+ try {
180
+ final Class <?> clazz = strategy .run (loader , className );
181
+ if (clazz != null ) {
182
+ return clazz ;
183
+ }
184
+ } catch (final Exception ignored ) {
185
+ // Do nothing
186
+ }
187
+ }
188
+ return null ;
191
189
}
192
190
}
191
+
192
+ private static final ClassLoadingStrategy [] CLASS_LOADING_STRATEGIES = {
193
+ // 1. Try the passed class loader
194
+ (loader , className ) -> loader != null ? loader .loadClass (className ) : null ,
195
+ // 2. Try the `LoaderUtil` magic
196
+ (loader , className ) -> LoaderUtil .loadClass (className ),
197
+ // 3. Try the current class loader
198
+ (loader , className ) ->
199
+ ThrowableExtendedStackTraceRenderer .class .getClassLoader ().loadClass (className )
200
+ };
201
+
202
+ private interface ClassLoadingStrategy {
203
+
204
+ Class <?> run (final ClassLoader loader , final String className ) throws Exception ;
205
+ }
193
206
}
0 commit comments