Boost Spring Boot Performance: Master Logback Async Logging & File Separation
This article explains how to configure Logback in a Spring Boot application to separate logs by level into different files, implement asynchronous logging to reduce disk I/O, and demonstrates a performance test showing a ten‑fold throughput increase, while also detailing the underlying async mechanism.
Logback Configuration and Asynchronous Logging in Spring Boot
Spring Boot includes Logback and SLF4J by default, so you only need to create a configuration file without adding extra dependencies.
Logback automatically loads a file named logback-spring.xml or logback.xml from the classpath. Storing all logs in a single file makes the file grow indefinitely and hampers troubleshooting; the proper approach is to separate error logs from other logs and rotate them by time.
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<property resource="logback.properties"/>
<appender name="CONSOLE-LOG" class="ch.qos.logback.core.ConsoleAppender">
<layout class="ch.qos.logback.classic.PatternLayout">
<pattern>[%d{yyyy-MM-dd' 'HH:mm:ss.SSS}] [%C] [%t] [%L] [%-5p] %m%n</pattern>
</layout>
</appender>
<!-- Get logs higher than INFO (including INFO) but exclude ERROR -->
<appender name="INFO-LOG" class="ch.qos.logback.core.rolling.RollingFileAppender">
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERROR</level>
<onMatch>DENY</onMatch>
<onMismatch>ACCEPT</onMismatch>
</filter>
<encoder>
<pattern>[%d{yyyy-MM-dd' 'HH:mm:ss.SSS}] [%C] [%t] [%L] [%-5p] %m%n</pattern>
</encoder>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOG_INFO_HOME}/%d.log</fileNamePattern>
<maxHistory>30</maxHistory>
</rollingPolicy>
</appender>
<appender name="ERROR-LOG" class="ch.qos.logback.core.rolling.RollingFileAppender">
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>ERROR</level>
</filter>
<encoder>
<pattern>[%d{yyyy-MM-dd' 'HH:mm:ss.SSS}] [%C] [%t] [%L] [%-5p] %m%n</pattern>
</encoder>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOG_ERROR_HOME}/%d.log</fileNamePattern>
<maxHistory>30</maxHistory>
</rollingPolicy>
</appender>
<root level="info">
<appender-ref ref="CONSOLE-LOG"/>
<appender-ref ref="INFO-LOG"/>
<appender-ref ref="ERROR-LOG"/>
</root>
</configuration>Key Tags Explanation
<root>– required tag that sets the base logging level. <appender> – defines an appender; the name attribute gives it a name, and the class attribute selects the output strategy (e.g., ConsoleAppender for console, RollingFileAppender for file). <filter> – specifies a filtering strategy, such as LevelFilter or ThresholdFilter. <encoder> – defines the log output format. <rollingPolicy> – configures log rotation, for example time‑based rolling with a file name pattern and a retention period.
Asynchronous Logging
Traditional logging is synchronous; each log write incurs a disk I/O operation that blocks the thread. By adding an asynchronous appender that forwards logs to an existing appender, the logging work is off‑loaded to a separate worker thread, eliminating the I/O block in the main thread.
<!-- Async output -->
<appender name="ASYNC-INFO" class="ch.qos.logback.classic.AsyncAppender">
<discardingThreshold>0</discardingThreshold>
<queueSize>256</queueSize>
<appender-ref ref="INFO-LOG"/>
</appender>
<appender name="ASYNC-ERROR" class="ch.qos.logback.classic.AsyncAppender">
<discardingThreshold>0</discardingThreshold>
<queueSize>256</queueSize>
<appender-ref ref="ERROR-LOG"/>
</appender>Performance Test
Hardware: 6‑core CPU, 8 GB RAM. Tool: Apache JMeter.
Synchronous logging test (100 threads, ramp‑up 0) yielded a throughput of 44.2 requests per second.
Asynchronous logging test (same load) produced a throughput of 497.5 requests per second, more than ten times higher.
How Asynchronous Logging Works
When Logger.info(...) is called, the event is passed to AsyncAppenderBase.append(). If the queue is not beyond the discarding threshold, the event is pre‑processed and placed into a blocking queue (default ArrayBlockingQueue size 256). A dedicated worker thread takes events from the queue and invokes appendLoopOnAppenders, which encodes the event and writes it to the target file.
protected void append(E eventObject) {
if (!this.isQueueBelowDiscardingThreshold() || !this.isDiscardable(eventObject)) {
this.preprocess(eventObject);
this.put(eventObject);
}
} E e = parent.blockingQueue.take();
aai.appendLoopOnAppenders(e);The main thread continues execution after enqueuing the log event, while the worker thread handles the actual I/O.
Reference
https://github.com/TiantianUpup/springboot-log
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.
Programmer DD
A tinkering programmer and author of "Spring Cloud Microservices in Action"
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.
