ITADN

NullPointerException when Throwable's hashCode returns different values

#4143OpenZschimmer 创建于 2026-06-11
bugduplicatelayoutswaiting-for-maintainer
Z
Zschimmercommented
## Description When a Throwable is logged asynchronously and shortly after this changed such that the Throwable's hashCode returns a different calculated value, a NullPointerException is thrown in background by the asynchronous logger, and the line is not logged. This happen's with ScalaTest. A failing test throws a ScalaTest exception. When the stack trace is shortened after logging, the ScalaTest exception recalculates the hashCode based on the changed stack trace. ## Configuration **Version:** 2.26.0 **Operating system:** macOS 26.5.1 **JDK:** 26.0.1+8-34 ## Logs ``` 2026-06-11T05:25:09.861732Z Log4j2-TF-1-AsyncLogger[AsyncContext@41e986c9]-1 ERROR An exception occurred processing Appender file java.lang.NullPointerException: Cannot read field "stackTrace" because "metadata" is null at org.apache.logging.log4j.core.pattern.ThrowableStackTraceRenderer.renderStackTraceElements(ThrowableStackTraceRenderer.java:151) at org.apache.logging.log4j.core.pattern.ThrowableStackTraceRenderer.renderThrowable(ThrowableStackTraceRenderer.java:101) at org.apache.logging.log4j.core.pattern.ThrowableStackTraceRenderer.renderThrowable(ThrowableStackTraceRenderer.java:77) at org.apache.logging.log4j.core.pattern.ThrowableStackTraceRenderer.renderThrowable(ThrowableStackTraceRenderer.java:56) at org.apache.logging.log4j.core.pattern.ThrowablePatternConverter.format(ThrowablePatternConverter.java:130) at org.apache.logging.log4j.core.layout.PatternLayout$NoFormatPatternSerializer.toSerializable(PatternLayout.java:354) at org.apache.logging.log4j.core.layout.PatternLayout.toText(PatternLayout.java:251) at org.apache.logging.log4j.core.layout.PatternLayout.encode(PatternLayout.java:237) at org.apache.logging.log4j.core.layout.PatternLayout.encode(PatternLayout.java:57) at org.apache.logging.log4j.core.appender.AbstractOutputStreamAppender.directEncodeEvent(AbstractOutputStreamAppender.java:227) at org.apache.logging.log4j.core.appender.AbstractOutputStreamAppender.tryAppend(AbstractOutputStreamAppender.java:220) at org.apache.logging.log4j.core.appender.AbstractOutputStreamAppender.append(AbstractOutputStreamAppender.java:211) at org.apache.logging.log4j.core.config.AppenderControl.tryCallAppender(AppenderControl.java:160) at org.apache.logging.log4j.core.config.AppenderControl.callAppender0(AppenderControl.java:133) at org.apache.logging.log4j.core.config.AppenderControl.callAppenderPreventRecursion(AppenderControl.java:124) at org.apache.logging.log4j.core.config.AppenderControl.callAppender(AppenderControl.java:88) at org.apache.logging.log4j.core.config.LoggerConfig.callAppenders(LoggerConfig.java:807) at org.apache.logging.log4j.core.config.LoggerConfig.processLogEvent(LoggerConfig.java:765) at org.apache.logging.log4j.core.config.LoggerConfig.log(LoggerConfig.java:741) at org.apache.logging.log4j.core.config.LoggerConfig.log(LoggerConfig.java:729) at org.apache.logging.log4j.core.config.AwaitCompletionReliabilityStrategy.log(AwaitCompletionReliabilityStrategy.java:108) at org.apache.logging.log4j.core.async.AsyncLogger.actualAsyncLog(AsyncLogger.java:568) at org.apache.logging.log4j.core.async.RingBufferLogEvent.execute(RingBufferLogEvent.java:172) at org.apache.logging.log4j.core.async.RingBufferLogEventHandler4.onEvent(RingBufferLogEventHandler4.java:54) at org.apache.logging.log4j.core.async.RingBufferLogEventHandler.onEvent(RingBufferLogEventHandler.java:32) at org.apache.logging.log4j.core.async.RingBufferLogEventHandler4.onEvent(RingBufferLogEventHandler4.java:31) at com.lmax.disruptor.BatchEventProcessor.processEvents(BatchEventProcessor.java:168) at com.lmax.disruptor.BatchEventProcessor.run(BatchEventProcessor.java:125) at java.base/java.lang.Thread.run(Thread.java:1516) ``` ## Reproduction Reproduced with Scala because the test starts log4j2 with it's own log4j2.xml. ``` scala - <<< ' //> using dep org.apache.logging.log4j:log4j-api:2.26.0 //> using dep org.apache.logging.log4j:log4j-core:2.26.0 //> using dep org.apache.logging.log4j:log4j-slf4j2-impl:2.26.0 //> using dep com.lmax:disruptor:3.4.4 import java.lang.System.setProperty import java.nio.charset.StandardCharsets.UTF_8 import java.nio.file.{Files, Path} val config = """<?xml version="1.0" encoding="UTF-8"?> |<configuration status="WARN"> | <appenders> | <randomAccessFile | name="file" | fileName="/tmp/TestLog4jNullPointerException.log" | append="false" | immediateFlush="false"/> | </appenders> | | <loggers> | <root level="trace"> | <appenderRef level="trace" ref="file"/> | </root> | </loggers> |</configuration> |""".stripMargin final class MyException extends Exception: private var counter = 0 override def hashCode = counter += 1 counter val file: Path = Files.createTempFile("TestLog4jNullPointerException-", "xml") try Files.write(file, config.getBytes(UTF_8)) setProperty("log4j2.configurationFile", file.toString) setProperty("log4j2.contextSelector", "org.apache.logging.log4j.core.async.AsyncLoggerContextSelector") val logger = org.apache.logging.log4j.LogManager.getLogger("TestLog4jNullPointerException") val exception = new MyException logger.info("TEST", exception) finally Files.deleteIfExists(file) ' ```
5 条评论