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>