This is an automated email from the ASF dual-hosted git repository.

vy pushed a commit to branch 2.x
in repository https://gitbox.apache.org/repos/asf/logging-log4j2.git


The following commit(s) were added to refs/heads/2.x by this push:
     new 466f98f471 Catch `LinkageError` in 
`ThrowableExtendedStackTraceRenderer#loadClass` (#4028) (#4049)
466f98f471 is described below

commit 466f98f47165444e6bae8181269d0b03c98b19a8
Author: CHANHAN <[email protected]>
AuthorDate: Mon Aug 31 18:07:50 2026 +0900

    Catch `LinkageError` in `ThrowableExtendedStackTraceRenderer#loadClass` 
(#4028) (#4049)
    
    Co-authored-by: Volkan Yazıcı <[email protected]>
---
 .../src/test/java/foo/TestFriendlyException.java   | 74 +++++++++++++++-------
 .../ExtendedThrowablePatternConverterTest.java     |  3 +
 .../pattern/RootThrowablePatternConverterTest.java |  3 +
 .../pattern/ThrowablePatternConverterTest.java     |  3 +
 .../ThrowableExtendedStackTraceRenderer.java       |  2 +-
 ...rror_in_ThrowableExtendedStackTraceRenderer.xml | 10 +++
 6 files changed, 72 insertions(+), 23 deletions(-)

diff --git a/log4j-core-test/src/test/java/foo/TestFriendlyException.java 
b/log4j-core-test/src/test/java/foo/TestFriendlyException.java
index c024db10ae..5d451c6a54 100644
--- a/log4j-core-test/src/test/java/foo/TestFriendlyException.java
+++ b/log4j-core-test/src/test/java/foo/TestFriendlyException.java
@@ -39,6 +39,7 @@ import org.apache.logging.log4j.util.Constants;
  * <li>Suppressed exceptions</li>
  * <li>Clutter-free stack trace (i.e., elements from JUnit, JDK, etc.)</li>
  * <li>Stack trace elements from named modules<sup>3</sup></li>
+ * <li>Stack trace elements of non-existent classes<sup>4</sup></li>
  * <li>Exceptions with malfunctioning (e.g., colliding) {@link 
Object#equals(Object) equals()} and {@link Object#hashCode() hashCode()} 
implementations in the causal chain</li>
  * </ul>
  * <p>
@@ -50,6 +51,10 @@ import org.apache.logging.log4j.util.Constants;
  * <p>
  * <sup>3</sup> Helps with testing module name serialization.
  * </p>
+ * <p>
+ * <sup>4</sup> Helps with testing non-{@link Exception} types, e.g., {@link 
LinkageError} and {@link NoClassDefFoundError}.
+ * See <a 
href="https://github.com/apache/logging-log4j2/issues/4028";>#4028</a> for 
details.
+ * </p>
  */
 public final class TestFriendlyException extends RuntimeException {
 
@@ -83,12 +88,15 @@ public final class TestFriendlyException extends 
RuntimeException {
         throw new IllegalStateException("should not have reached here");
     }
 
+    public static final StackTraceElement 
NON_EXISTENT_CLASS_STACK_TRACE_ELEMENT =
+            new 
StackTraceElement("com.nonexistent.deliberately.missing.ClassName", 
"someMethod", "ClassName.java", 42);
+
     private static final String[] EXCLUDED_CLASS_NAME_PREFIXES = {
         "java.lang", "jdk.internal", "org.junit", "sun.reflect"
     };
 
     public static final TestFriendlyException INSTANCE =
-            create("r", 0, 2, new boolean[] {false}, new boolean[] {true}, new 
int[] {5});
+            create("r", 0, 2, new boolean[] {false}, new boolean[] {true}, new 
boolean[] {true}, new int[] {5});
 
     static {
         ensureIdentityMalfunctionAtDifferentDepths();
@@ -105,21 +113,15 @@ public final class TestFriendlyException extends 
RuntimeException {
         final Queue<TestFriendlyException> exceptions = new LinkedList<>();
         exceptions.add(INSTANCE);
         while (!exceptions.isEmpty()) {
-
-            // Process the exception
             final TestFriendlyException exception = exceptions.remove();
             if (!visitedExceptions.add(exception) || 
!exception.identityMalfunctioning) {
                 continue;
             }
             
identityMalfunctioningExceptionStackTraceDepths.add(exception.getStackTrace().length);
-
-            // Enqueue the cause
             final TestFriendlyException cause = (TestFriendlyException) 
exception.getCause();
             if (cause != null) {
                 exceptions.add(cause);
             }
-
-            // Enqueue the suppressed
             for (final Throwable suppressed : exception.getSuppressed()) {
                 exceptions.add((TestFriendlyException) suppressed);
             }
@@ -135,15 +137,28 @@ public final class TestFriendlyException extends 
RuntimeException {
             final int maxDepth,
             final boolean[] circular,
             final boolean[] namedModuleAllowed,
+            final boolean[] nonExistentClassAllowed,
             final int[] maxIdentityMalfunctionCount) {
-        final TestFriendlyException error =
-                new TestFriendlyException(name, namedModuleAllowed, 
maxIdentityMalfunctionCount);
+        final TestFriendlyException error = new TestFriendlyException(
+                name, namedModuleAllowed, nonExistentClassAllowed, 
maxIdentityMalfunctionCount);
         if (depth < maxDepth) {
-            final TestFriendlyException cause =
-                    create(name + "_c", depth + 1, maxDepth, circular, 
namedModuleAllowed, maxIdentityMalfunctionCount);
+            final TestFriendlyException cause = create(
+                    name + "_c",
+                    depth + 1,
+                    maxDepth,
+                    circular,
+                    namedModuleAllowed,
+                    nonExistentClassAllowed,
+                    maxIdentityMalfunctionCount);
             error.initCause(cause);
-            final TestFriendlyException suppressed =
-                    create(name + "_s", depth + 1, maxDepth, circular, 
namedModuleAllowed, maxIdentityMalfunctionCount);
+            final TestFriendlyException suppressed = create(
+                    name + "_s",
+                    depth + 1,
+                    maxDepth,
+                    circular,
+                    namedModuleAllowed,
+                    nonExistentClassAllowed,
+                    maxIdentityMalfunctionCount);
             error.addSuppressed(suppressed);
             final boolean circularAllowed = depth + 1 == maxDepth && 
!circular[0];
             if (circularAllowed) {
@@ -158,18 +173,22 @@ public final class TestFriendlyException extends 
RuntimeException {
     private final boolean identityMalfunctioning;
 
     private TestFriendlyException(
-            final String message, final boolean[] namedModuleAllowed, final 
int[] maxIdentityMalfunctionCount) {
+            final String message,
+            final boolean[] namedModuleAllowed,
+            final boolean[] nonExistentClassAllowed,
+            final int[] maxIdentityMalfunctionCount) {
         super(message);
         this.identityMalfunctioning = --maxIdentityMalfunctionCount[0] > 0;
-        removeExcludedStackTraceElements(namedModuleAllowed);
+        removeExcludedStackTraceElements(namedModuleAllowed, 
nonExistentClassAllowed);
     }
 
-    private void removeExcludedStackTraceElements(final boolean[] 
namedModuleAllowed) {
+    private void removeExcludedStackTraceElements(
+            final boolean[] namedModuleAllowed, final boolean[] 
nonExistentClassAllowed) {
         final StackTraceElement[] oldStackTrace = getStackTrace();
         final boolean[] seenExcludedStackTraceElement = {false};
         final StackTraceElement[] newStackTrace = Arrays.stream(oldStackTrace)
-                .flatMap(stackTraceElement ->
-                        mapStackTraceElement(stackTraceElement, 
namedModuleAllowed, seenExcludedStackTraceElement))
+                .flatMap(stackTraceElement -> mapStackTraceElement(
+                        stackTraceElement, namedModuleAllowed, 
nonExistentClassAllowed, seenExcludedStackTraceElement))
                 .toArray(StackTraceElement[]::new);
         setStackTrace(newStackTrace);
     }
@@ -177,12 +196,14 @@ public final class TestFriendlyException extends 
RuntimeException {
     private static Stream<StackTraceElement> mapStackTraceElement(
             final StackTraceElement stackTraceElement,
             final boolean[] namedModuleAllowed,
+            final boolean[] nonExistentClassAllowed,
             final boolean[] seenExcludedStackTraceElement) {
-        final Stream<StackTraceElement> filteredStackTraceElement =
+        final Stream<StackTraceElement> filteredStackTraceElements =
                 filterStackTraceElement(stackTraceElement, 
seenExcludedStackTraceElement);
-        final Stream<StackTraceElement> javaBaseIncludedStackTraceElement =
-                namedModuleIncludedStackTraceElement(namedModuleAllowed);
-        return Stream.concat(javaBaseIncludedStackTraceElement, 
filteredStackTraceElement);
+        final Stream<StackTraceElement> optionalStackTraceElements = 
Stream.concat(
+                namedModuleIncludedStackTraceElement(namedModuleAllowed),
+                
nonExistentClassIncludedStackTraceElement(nonExistentClassAllowed));
+        return Stream.concat(optionalStackTraceElements, 
filteredStackTraceElements);
     }
 
     private static Stream<StackTraceElement> filterStackTraceElement(
@@ -221,6 +242,15 @@ public final class TestFriendlyException extends 
RuntimeException {
         return Stream.of(NAMED_MODULE_STACK_TRACE_ELEMENT);
     }
 
+    private static Stream<StackTraceElement> 
nonExistentClassIncludedStackTraceElement(
+            final boolean[] nonExistentClassAllowed) {
+        if (!nonExistentClassAllowed[0]) {
+            return Stream.of();
+        }
+        nonExistentClassAllowed[0] = false;
+        return Stream.of(NON_EXISTENT_CLASS_STACK_TRACE_ELEMENT);
+    }
+
     @Override
     public String getLocalizedMessage() {
         return getMessage() + " [localized]";
diff --git 
a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/pattern/ExtendedThrowablePatternConverterTest.java
 
b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/pattern/ExtendedThrowablePatternConverterTest.java
index ea71b62544..edffb79bc2 100644
--- 
a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/pattern/ExtendedThrowablePatternConverterTest.java
+++ 
b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/pattern/ExtendedThrowablePatternConverterTest.java
@@ -44,6 +44,7 @@ class ExtendedThrowablePatternConverterTest {
     private static final List<String> EXPECTED_FULL_STACK_TRACE_LINES = asList(
             "foo.TestFriendlyException: r [localized]",
             "  at " + TestFriendlyException.NAMED_MODULE_STACK_TRACE_ELEMENT + 
" ~[?:?]",
+            "\tat " + 
TestFriendlyException.NON_EXISTENT_CLASS_STACK_TRACE_ELEMENT + " ~[?:?]",
             "  at 
foo.TestFriendlyException.create(TestFriendlyException.java:0) 
~[test-classes/:?]",
             "  at 
foo.TestFriendlyException.<clinit>(TestFriendlyException.java:0) 
~[test-classes/:?]",
             "  at " + 
TestFriendlyException.ORG_APACHE_REPLACEMENT_STACK_TRACE_ELEMENT + " ~[?:0]",
@@ -109,6 +110,7 @@ class ExtendedThrowablePatternConverterTest {
                     asList(
                             "foo.TestFriendlyException: r [localized]",
                             "  at " + 
TestFriendlyException.NAMED_MODULE_STACK_TRACE_ELEMENT + " ~[?:?]",
+                            "\tat " + 
TestFriendlyException.NON_EXISTENT_CLASS_STACK_TRACE_ELEMENT + " ~[?:?]",
                             "  ... suppressed 2 lines",
                             "  at " + 
TestFriendlyException.ORG_APACHE_REPLACEMENT_STACK_TRACE_ELEMENT + " ~[?:0]",
                             "  Suppressed: foo.TestFriendlyException: r_s 
[localized]",
@@ -145,6 +147,7 @@ class ExtendedThrowablePatternConverterTest {
                     asList(
                             "foo.TestFriendlyException: r [localized]",
                             "  at " + 
TestFriendlyException.NAMED_MODULE_STACK_TRACE_ELEMENT + " ~[?:?]",
+                            "\tat " + 
TestFriendlyException.NON_EXISTENT_CLASS_STACK_TRACE_ELEMENT + " ~[?:?]",
                             "  at 
foo.TestFriendlyException.create(TestFriendlyException.java:0) 
~[test-classes/:?]",
                             "  at 
foo.TestFriendlyException.<clinit>(TestFriendlyException.java:0) 
~[test-classes/:?]",
                             "  ...",
diff --git 
a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/pattern/RootThrowablePatternConverterTest.java
 
b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/pattern/RootThrowablePatternConverterTest.java
index 80e4f008bf..992b3fc3a3 100644
--- 
a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/pattern/RootThrowablePatternConverterTest.java
+++ 
b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/pattern/RootThrowablePatternConverterTest.java
@@ -66,6 +66,7 @@ class RootThrowablePatternConverterTest {
             "          ... 3 more",
             "Wrapped by: foo.TestFriendlyException: r [localized]",
             "  at " + TestFriendlyException.NAMED_MODULE_STACK_TRACE_ELEMENT,
+            "\tat " + 
TestFriendlyException.NON_EXISTENT_CLASS_STACK_TRACE_ELEMENT,
             "  at 
foo.TestFriendlyException.create(TestFriendlyException.java:0)",
             "  at 
foo.TestFriendlyException.<clinit>(TestFriendlyException.java:0)",
             "  at " + 
TestFriendlyException.ORG_APACHE_REPLACEMENT_STACK_TRACE_ELEMENT,
@@ -138,6 +139,7 @@ class RootThrowablePatternConverterTest {
                             "          ... 3 more",
                             "Wrapped by: foo.TestFriendlyException: r 
[localized]",
                             "  at " + 
TestFriendlyException.NAMED_MODULE_STACK_TRACE_ELEMENT,
+                            "\tat " + 
TestFriendlyException.NON_EXISTENT_CLASS_STACK_TRACE_ELEMENT,
                             "  ... suppressed 2 lines",
                             "  at " + 
TestFriendlyException.ORG_APACHE_REPLACEMENT_STACK_TRACE_ELEMENT,
                             "  Suppressed: foo.TestFriendlyException: r_s_c 
[localized]",
@@ -177,6 +179,7 @@ class RootThrowablePatternConverterTest {
                             "          ... 3 more",
                             "Wrapped by: foo.TestFriendlyException: r 
[localized]",
                             "  at " + 
TestFriendlyException.NAMED_MODULE_STACK_TRACE_ELEMENT,
+                            "\tat " + 
TestFriendlyException.NON_EXISTENT_CLASS_STACK_TRACE_ELEMENT,
                             "  at 
foo.TestFriendlyException.create(TestFriendlyException.java:0)",
                             "  at 
foo.TestFriendlyException.<clinit>(TestFriendlyException.java:0)",
                             "  ...",
diff --git 
a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/pattern/ThrowablePatternConverterTest.java
 
b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/pattern/ThrowablePatternConverterTest.java
index 0ce38d0323..20875a0e3e 100644
--- 
a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/pattern/ThrowablePatternConverterTest.java
+++ 
b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/pattern/ThrowablePatternConverterTest.java
@@ -304,6 +304,7 @@ public class ThrowablePatternConverterTest {
                     asList(
                             "foo.TestFriendlyException: r [localized]",
                             "  at " + 
TestFriendlyException.NAMED_MODULE_STACK_TRACE_ELEMENT,
+                            "\tat " + 
TestFriendlyException.NON_EXISTENT_CLASS_STACK_TRACE_ELEMENT,
                             "  at 
foo.TestFriendlyException.create(TestFriendlyException.java:0)",
                             "  at 
foo.TestFriendlyException.<clinit>(TestFriendlyException.java:0)",
                             "  at " + 
TestFriendlyException.ORG_APACHE_REPLACEMENT_STACK_TRACE_ELEMENT,
@@ -347,6 +348,7 @@ public class ThrowablePatternConverterTest {
                     asList(
                             "foo.TestFriendlyException: r [localized]",
                             "  at " + 
TestFriendlyException.NAMED_MODULE_STACK_TRACE_ELEMENT,
+                            "\tat " + 
TestFriendlyException.NON_EXISTENT_CLASS_STACK_TRACE_ELEMENT,
                             "  ... suppressed 2 lines",
                             "  at " + 
TestFriendlyException.ORG_APACHE_REPLACEMENT_STACK_TRACE_ELEMENT,
                             "  Suppressed: foo.TestFriendlyException: r_s 
[localized]",
@@ -383,6 +385,7 @@ public class ThrowablePatternConverterTest {
                     asList(
                             "foo.TestFriendlyException: r [localized]",
                             "  at " + 
TestFriendlyException.NAMED_MODULE_STACK_TRACE_ELEMENT,
+                            "\tat " + 
TestFriendlyException.NON_EXISTENT_CLASS_STACK_TRACE_ELEMENT,
                             "  at 
foo.TestFriendlyException.create(TestFriendlyException.java:0)",
                             "  at 
foo.TestFriendlyException.<clinit>(TestFriendlyException.java:0)",
                             "  ...",
diff --git 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/ThrowableExtendedStackTraceRenderer.java
 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/ThrowableExtendedStackTraceRenderer.java
index c70e57d4d4..2c7ea334bb 100644
--- 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/ThrowableExtendedStackTraceRenderer.java
+++ 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/ThrowableExtendedStackTraceRenderer.java
@@ -181,7 +181,7 @@ final class ThrowableExtendedStackTraceRenderer
                     if (clazz != null) {
                         return clazz;
                     }
-                } catch (final Exception ignored) {
+                } catch (final Throwable ignored) {
                     // Do nothing
                 }
             }
diff --git 
a/src/changelog/.2.x.x/4049_fix_catch_LinkageError_in_ThrowableExtendedStackTraceRenderer.xml
 
b/src/changelog/.2.x.x/4049_fix_catch_LinkageError_in_ThrowableExtendedStackTraceRenderer.xml
new file mode 100644
index 0000000000..a4a7be90a5
--- /dev/null
+++ 
b/src/changelog/.2.x.x/4049_fix_catch_LinkageError_in_ThrowableExtendedStackTraceRenderer.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<entry xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
+       xmlns="https://logging.apache.org/xml/ns";
+       xsi:schemaLocation="https://logging.apache.org/xml/ns
+       https://logging.apache.org/xml/ns/log4j-changelog-0.xsd";
+       type="fixed">
+    <issue id="4028" 
link="https://github.com/apache/logging-log4j2/issues/4028"/>
+    <issue id="4049" 
link="https://github.com/apache/logging-log4j2/pull/4049"/>
+    <description format="asciidoc">Ensure all `Throwable`s are handled while 
rendering stack traces</description>
+</entry>

Reply via email to