Boost Spring Boot Performance: Master Logback Async Logging & File Segmentation
Learn how to configure Logback in Spring Boot to separate logs by level, implement asynchronous logging for reduced I/O latency, and verify performance gains—up to tenfold throughput improvement—using JMeter tests on a six‑core, 8 GB server, with detailed XML snippets and code explanations.
01 What You Will Learn
Log output to files and categorize them by LEVEL for separate storage.
Reduce disk IO by using asynchronous logging to improve performance.
Understand the principle behind asynchronous log output.
02 Configure logback-spring.xml
A Spring Boot project already includes logback and slf4j dependencies, so you only need to write the configuration file. Logback automatically loads a file named logback-spring.xml or logback.xml from the classpath. Instead of storing all logs in a single growing file, you should separate error logs from other levels and rotate logs 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>
<!-- Appender for INFO and higher (excluding 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>
<!-- Rolling policy -->
<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>
<!-- Rolling policy -->
<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 explained: <root>: mandatory tag that sets the base logging level. <appender-ref>: adds an appender to the logger. name attribute: names the appender. class attribute: specifies the output strategy, e.g., ConsoleAppender for console output or a file appender for persistence. <filter>: defines a filtering strategy, such as <level> to filter by log level. <encoder> with <pattern>: sets the log format. <rollingPolicy>: configures time‑based log rotation.
03 Logback Advanced Feature: Asynchronous Logging
Traditional logging is synchronous, causing a disk I/O operation for each log entry. By adding an asynchronous appender that references the original file appender, you can avoid blocking the application thread.
<!-- Async output -->
<appender name="ASYNC-INFO" class="ch.qos.logback.classic.AsyncAppender">
<!-- Do not discard logs; default discarding threshold is 0 -->
<discardingThreshold>0</discardingThreshold>
<!-- Queue depth (default 256) can be tuned for performance -->
<queueSize>256</queueSize>
<!-- Reference the original INFO appender -->
<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>04 Asynchronous Logging Performance Test
To verify the performance gain, a JMeter test with 100 concurrent threads and a ramp‑up of 0 seconds was conducted.
Synchronous Logging
Throughput (TPS) was 44.2 requests/second.
Asynchronous Logging
Throughput (TPS) increased to 497.5 requests/second, more than ten times faster.
05 Server Hardware
CPU: six cores
Memory: 8 GB
06 Test Tool
Apache JMeter was used for the load test.
07 Synchronous Logging Details
Thread count: 100
Ramp‑up: 0 (all threads start simultaneously)
Result image shown above.
08 Asynchronous Logging Details
Thread count: 100
Ramp‑up: 0
Result image shown above.
09 Asynchronous Logging Principle
The flow starts from Logger.info in Logback, which quickly enqueues the log event. The AsyncAppenderBase uses an ArrayBlockingQueue (default size 256, configurable) to hold events.
protected void append(E eventObject) {
if (!this.isQueueBelowDiscardingThreshold() || !this.isDiscardable(eventObject)) {
this.preprocess(eventObject);
this.put(eventObject);
}
}A dedicated worker thread takes events from the queue and forwards them to the attached appenders:
E e = parent.blockingQueue.take();
aai.appendLoopOnAppenders(e);The worker ultimately calls the encode and write methods of each appender to persist the 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.
