Affected Versions
All versions.
Description
When logging an exception, for example using logger.error("error", e), an IllegalArgumentException can be thrown from within Logback's internal processing.
Here is a typical stack trace:
java.lang.IllegalArgumentException: ste cannot be null
at ch.qos.logback.classic.spi.StackTraceElementProxy.<init>(StackTraceElementProxy.java:30)
at ch.qos.logback.classic.spi.ThrowableProxyUtil.steArrayToStepArray(ThrowableProxyUtil.java:49)
at ch.qos.logback.classic.spi.ThrowableProxy.<init>(ThrowableProxy.java:55)
at ch.qos.logback.classic.spi.ThrowableProxy.<init>(ThrowableProxy.java:60)
at ch.qos.logback.classic.spi.ThrowableProxy.<init>(ThrowableProxy.java:60)
at ch.qos.logback.classic.spi.ThrowableProxy.<init>(ThrowableProxy.java:60)
at ch.qos.logback.classic.spi.LoggingEvent.<init>(LoggingEvent.java:119)
at ch.qos.logback.classic.Logger.buildLoggingEventAndAppend(Logger.java:419)
at ch.qos.logback.classic.Logger.filterAndLog_0_Or3Plus(Logger.java:383)
at ch.qos.logback.classic.Logger.error(Logger.java:538)
Root Cause
The exception originates from the StackTraceElementProxy constructor, which validates that its StackTraceElement argument is not null.
// ch.qos.logback.classic.spi.StackTraceElementProxy
public StackTraceElementProxy(StackTraceElement ste) {
if (ste == null) {
throw new IllegalArgumentException("ste cannot be null");
}
this.ste = ste;
}
This constructor is called by ThrowableProxyUtil.steArrayToStepArray. The current implementation of this method iterates over an array of StackTraceElements but does not handle cases where elements in the array are null.
// ch.qos.logback.classic.spi.ThrowableProxyUtil (current implementation)
static StackTraceElementProxy[] steArrayToStepArray(StackTraceElement[] stea) {
if (stea == null) {
return new StackTraceElementProxy[0];
}
StackTraceElementProxy[] stepa = new StackTraceElementProxy[stea.length];
for (int i = 0; i < stepa.length; i++) {
// This line throws an exception if stea[i] is null
stepa[i] = new StackTraceElementProxy(stea[i]);
}
return stepa;
}
Scenario
Although the JVM specification implies that Throwable.getStackTrace() should not return an array containing null elements, we have observed this behavior in a Jakarta EE environment.
The issue occurs intermittently when an exception is thrown from an EJB service running in a new transaction (i.e., with @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)). When the caller catches and logs this exception, the StackTraceElement[] array obtained from it sometimes contains null elements.
While the root of this problem may lie within the Jakarta EE platform, a logging framework should be robust enough to handle such real-world scenarios gracefully instead of crashing.
Proposed Solution
To prevent this crash, we propose modifying ThrowableProxyUtil.steArrayToStepArray to filter out null StackTraceElement objects. This ensures StackTraceElementProxy is never instantiated with a null argument.
Here is a suggested implementation that correctly handles null elements:
// ch.qos.logback.classic.spi.ThrowableProxyUtil
static StackTraceElementProxy[] steArrayToStepArray(StackTraceElement[] stea) {
if (stea == null) {
return new StackTraceElementProxy[0];
}
StackTraceElementProxy[] stepa = new StackTraceElementProxy[stea.length];
int stepaSkips = 0;
for (int i = 0; i < stepa.length; i++) {
StackTraceElement ste = stea[i];
if (ste == null) {
stepaSkips++;
} else {
stepa[i] = new StackTraceElementProxy(ste);
}
}
return (stepaSkips == 0) ? stepa : Arrays.copyOf(stepa, stepa.length - stepaSkips);
}
Affected Versions
All versions.
Description
When logging an exception, for example using
logger.error("error", e), anIllegalArgumentExceptioncan be thrown from within Logback's internal processing.Here is a typical stack trace:
Root Cause
The exception originates from the
StackTraceElementProxyconstructor, which validates that itsStackTraceElementargument is not null.This constructor is called by
ThrowableProxyUtil.steArrayToStepArray. The current implementation of this method iterates over an array ofStackTraceElements but does not handle cases where elements in the array arenull.Scenario
Although the JVM specification implies that
Throwable.getStackTrace()should not return an array containingnullelements, we have observed this behavior in a Jakarta EE environment.The issue occurs intermittently when an exception is thrown from an EJB service running in a new transaction (i.e., with
@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)). When the caller catches and logs this exception, theStackTraceElement[]array obtained from it sometimes containsnullelements.While the root of this problem may lie within the Jakarta EE platform, a logging framework should be robust enough to handle such real-world scenarios gracefully instead of crashing.
Proposed Solution
To prevent this crash, we propose modifying
ThrowableProxyUtil.steArrayToStepArrayto filter outnullStackTraceElementobjects. This ensuresStackTraceElementProxyis never instantiated with anullargument.Here is a suggested implementation that correctly handles
nullelements: