Analyzing ConsoleAppender Blocking and Using AsyncLogger for High-Performance Logging in Log4j2
This article investigates why using Log4j2's ConsoleAppender can block business threads, analyzes thread stack traces and performance bottlenecks, and demonstrates how switching to AsyncLogger with Disruptor dramatically improves logging throughput and eliminates service deadlocks.
Author: Du Yunjie, architect, responsible for service governance, MQ, cloud platform, APM, IM, distributed tracing, monitoring, configuration center, distributed task scheduling, ID generation, and distributed locks.
1. Background
Several services experienced deadlock situations, which were traced to the use of the ConsoleAppender of the logging framework in production.
2. Investigation Process
2.1 Stack of Blocked Threads
Thread dumps obtained via jstack showed many threads blocked on the monitor lock of
org.apache.logging.log4j.core.layout.TextEncoderHelper.copyDataToDestination. An example stack trace is shown below:
The block occurs when a thread attempts to acquire the lock of an OutputStreamManager (a subclass of ByteBufferDestination).
2.2 Stack of Threads Holding the Lock
The thread that holds the lock was also captured, revealing that business threads using ConsoleAppender need to synchronize on the OutputStreamManager monitor, then invoke PrintStream.write() → BufferedOutputStream.write() → FileOutputStream.write() and finally flush(). Only after the console write succeeds is the lock released, causing other threads to block.
The call sequence is illustrated below:
2.3 Console Log Performance Analysis
The synchronization around the console write is inefficient; benchmark data from the official site shows very low throughput for console logging.
3. AsyncLogger
Using a synchronous logger with ConsoleAppender blocks business threads, while the same applies to RollingRandomAccessFileAppender but with higher efficiency. The solution is to switch to AsyncLogger, which leverages the high‑performance Disruptor ring‑buffer to make logging asynchronous, eliminating lock contention and dramatically improving response time.
AsyncLogger workflow: business threads publish log events to the Disruptor ring buffer via tryPublishEvent() and return immediately without acquiring locks. A dedicated thread pool consumes the events and writes them to the destination.
Performance figures from the official documentation demonstrate the superiority of AsyncLogger.
3.1 How to Integrate
1) Add the Disruptor dependency:
<dependency>
<groupId>com.lmax</groupId>
<artifactId>disruptor</artifactId>
<version>3.4.2</version>
</dependency>2) Configure Log4j2 XML as follows:
<Loggers>
<!-- Set Spring logs to INFO -->
<AsyncLogger name="org.springframework" level="ERROR" includeLocation="true"/>
<asyncRoot level="INFO" includeLocation="true">
<AppenderRef ref="INFO"/>
<AppenderRef ref="WARN"/>
<AppenderRef ref="ERROR"/>
</asyncRoot>
</Loggers>4. Conclusion
Do not use ConsoleAppender in production.
Strongly recommend AsyncLogger for high‑performance logging.
Synchronous loggers can be used, but RollingRandomAccessFileAppender is preferred over ConsoleAppender due to higher efficiency.
The company has enforced a ban on ConsoleAppender and mandates AsyncLogger, eliminating service deadlocks caused by logging.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Zhuanzhuan Tech
A platform for Zhuanzhuan R&D and industry peers to learn and exchange technology, regularly sharing frontline experience and cutting‑edge topics. We welcome practical discussions and sharing; contact waterystone with any questions.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
