diff --git a/core/src/main/java/dev/faststats/ErrorHelper.java b/core/src/main/java/dev/faststats/ErrorHelper.java index d00ca25d..9c4ce83b 100644 --- a/core/src/main/java/dev/faststats/ErrorHelper.java +++ b/core/src/main/java/dev/faststats/ErrorHelper.java @@ -11,6 +11,7 @@ import java.util.List; import java.util.Locale; import java.util.Map; +import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.regex.Pattern; @@ -154,29 +155,37 @@ private static List collapseConsecutiveDuplicates(final List lin return result; } - public static boolean isSameLoader(final ClassLoader loader, final Throwable error) { - return isSameLoader(loader, error, Collections.newSetFromMap(new IdentityHashMap<>())); + public static boolean isSameLoader(final Thread thread, final ClassLoader loader, final Throwable error) { + return isSameLoader(thread, loader, error, Collections.newSetFromMap(new IdentityHashMap<>())); } - private static boolean isSameLoader(final ClassLoader loader, @Nullable final Throwable error, final Set visited) { + private static boolean isSameLoader(final Thread thread, final ClassLoader loader, @Nullable final Throwable error, final Set visited) { if (error == null || !visited.add(error)) return false; final var stackTrace = error.getStackTrace(); if (stackTrace == null || stackTrace.length == 0) - return isSameLoader(loader, error.getCause(), visited); + return isSameLoader(thread, loader, error.getCause(), visited); final var firstNonLibraryIndex = findFirstNonLibraryFrameIndex(stackTrace); - if (firstNonLibraryIndex == -1) return isSameLoader(loader, error.getCause(), visited); + if (firstNonLibraryIndex == -1) return isSameLoader(thread, loader, error.getCause(), visited); final var framesToCheck = Math.min(5, stackTrace.length - firstNonLibraryIndex); for (var i = 0; i < framesToCheck; i++) { final var frame = stackTrace[firstNonLibraryIndex + i]; if (isLibraryFrame(frame.getClassName())) continue; - if (!isFromLoader(frame, loader)) return isSameLoader(loader, error.getCause(), visited); + if (!isFromLoader(frame, loader)) return isSameLoader(thread, loader, error.getCause(), visited); } - return true; + return classLoadersMatch(thread.getContextClassLoader(), loader); + } + + private static boolean classLoadersMatch(@Nullable final ClassLoader first, @Nullable final ClassLoader second) { + if (Objects.equals(first, second)) return true; + if (first == null || second == null) return false; + if (classLoadersMatch(first.getParent(), second)) return true; + if (classLoadersMatch(first, second.getParent())) return true; + return false; } private static int findFirstNonLibraryFrameIndex(final StackTraceElement[] stackTrace) { diff --git a/core/src/main/java/dev/faststats/ErrorTracker.java b/core/src/main/java/dev/faststats/ErrorTracker.java index 687b1c7b..60fef2ac 100644 --- a/core/src/main/java/dev/faststats/ErrorTracker.java +++ b/core/src/main/java/dev/faststats/ErrorTracker.java @@ -15,14 +15,15 @@ */ public sealed interface ErrorTracker permits SimpleErrorTracker { /** - * Creates a context-aware error tracker policy. + * Creates a context-aware error tracker policy for the current class loader. * * @return the error tracker policy + * @see #contextAware(ClassLoader) * @since 0.24.0 */ @Contract(value = " -> new", pure = true) static ErrorTracker contextAware() { - return contextAware(ErrorTracker.class.getClassLoader()); + return contextAware(Thread.currentThread().getContextClassLoader()); } /** @@ -256,6 +257,20 @@ default ErrorTracker anonymize(@RegExp final String pattern, final String replac */ @Contract(pure = true) static boolean isSameLoader(final ClassLoader loader, final Throwable error) { - return ErrorHelper.isSameLoader(loader, error); + return ErrorHelper.isSameLoader(Thread.currentThread(), loader, error); + } + + /** + * Checks if the error occurred in the same class loader as the provided loader and thread. + * + * @param thread the thread + * @param loader the class loader + * @param error the error + * @return whether the error occurred in the same class loader + * @since 0.23.0 + */ + @Contract(pure = true) + static boolean isSameLoader(final Thread thread, final ClassLoader loader, final Throwable error) { + return ErrorHelper.isSameLoader(thread, loader, error); } } diff --git a/core/src/main/java/dev/faststats/SimpleErrorTrackerService.java b/core/src/main/java/dev/faststats/SimpleErrorTrackerService.java index 250ecfce..358b5cb9 100644 --- a/core/src/main/java/dev/faststats/SimpleErrorTrackerService.java +++ b/core/src/main/java/dev/faststats/SimpleErrorTrackerService.java @@ -58,7 +58,7 @@ private static void handleUncaughtException(final Thread thread, final Throwable for (final var tracker : DISPATCHER_TRACKERS) { try { final var loader = tracker.attachedLoader(); - if (loader != null && !ErrorHelper.isSameLoader(loader, error)) continue; + if (loader != null && !ErrorHelper.isSameLoader(thread, loader, error)) continue; tracker.trackError(error).handled(false); tracker.getContextErrorHandler().ifPresent(handler -> handler.accept(loader, error)); } catch (final Throwable t) {