Spring Boot
Log4j2
Log4j2 is a high-performance logging implementation that can replace Logback in Spring Boot. It offers an asynchronous logging architecture, a more expressive configuration format (XML, YAML, JSON, or Properties), Garbage-Free logging to reduce GC pressure, and a richer plugin system. This entry covers dependency setup, XML configuration, async loggers, structured JSON output, and migration from Logback.
Switching from Logback to Log4j2
Exclude spring-boot-starter-logging (which pulls in Logback) and add spring-boot-starter-log4j2. Spring Boot's auto-configuration detects Log4j2 on the classpath and uses it automatically. All SLF4J API calls and Spring Boot logging properties continue to work unchanged.
XML
<!-- pom.xml — exclude Logback, add Log4j2 ──────────────────────── -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<!-- Exclude the default Logback starter -->
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- Add Log4j2 starter -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
<!-- Optional: YAML configuration support for log4j2 -->
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-yaml</artifactId>
</dependency>
<!-- Gradle equivalent:
configurations {
all {
exclude group: "org.springframework.boot",
module: "spring-boot-starter-logging"
}
}
dependencies {
implementation "org.springframework.boot:spring-boot-starter-log4j2"
}
-->
# ── application.yml still works — Spring Boot translates properties ───
logging:
level:
root: INFO
com.myapp: DEBUG
org.hibernate.SQL: DEBUG
file:
name: logs/application.loglog4j2-spring.xml Configuration
Place log4j2-spring.xml in src/main/resources. The -spring suffix enables Spring's property placeholder resolution and profile support via the SpringLookup. Log4j2's configuration is more explicit than Logback but gains the Properties section for reusable values.
XML
<!-- src/main/resources/log4j2-spring.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN" monitorInterval="30">
<!-- ── Properties — reusable values ──────────────────────────────── -->
<Properties>
<Property name="LOG_PATTERN">
%d{yyyy-MM-dd HH:mm:ss.SSS} %-5p
[${spring:spring.application.name}]
[%X{correlationId}] %-40.40c{1.} : %m%n%ex
</Property>
<Property name="LOG_PATH">logs</Property>
<Property name="APP_NAME">
${spring:spring.application.name:-application}
</Property>
</Properties>
<!-- ── Appenders ──────────────────────────────────────────────────── -->
<Appenders>
<!-- Console appender -->
<Console name="CONSOLE" target="SYSTEM_OUT">
<PatternLayout pattern="${LOG_PATTERN}"/>
<!-- Threshold filter — only INFO+ to console -->
<ThresholdFilter level="INFO"
onMatch="ACCEPT" onMismatch="DENY"/>
</Console>
<!-- Rolling file appender -->
<RollingFile name="FILE"
fileName="${LOG_PATH}/${APP_NAME}.log"
filePattern="${LOG_PATH}/${APP_NAME}-%d{yyyy-MM-dd}.%i.log.gz">
<PatternLayout pattern="${LOG_PATTERN}"/>
<Policies>
<TimeBasedTriggeringPolicy interval="1"
modulate="true"/>
<SizeBasedTriggeringPolicy size="50MB"/>
</Policies>
<DefaultRolloverStrategy max="30"
compressionLevel="9">
<Delete basePath="${LOG_PATH}" maxDepth="1">
<IfLastModified age="30d"/>
<IfAccumulatedFileSize exceeds="2GB"/>
</Delete>
</DefaultRolloverStrategy>
</RollingFile>
<!-- Error-only appender -->
<RollingFile name="ERROR_FILE"
fileName="${LOG_PATH}/error.log"
filePattern="${LOG_PATH}/error-%d{yyyy-MM-dd}.%i.log.gz">
<LevelRangeFilter minLevel="ERROR"
maxLevel="ERROR" onMatch="ACCEPT" onMismatch="DENY"/>
<PatternLayout pattern="${LOG_PATTERN}"/>
<Policies>
<TimeBasedTriggeringPolicy/>
<SizeBasedTriggeringPolicy size="10MB"/>
</Policies>
<DefaultRolloverStrategy max="90"/>
</RollingFile>
</Appenders>
<!-- ── Loggers ────────────────────────────────────────────────────── -->
<Loggers>
<!-- Application packages -->
<Logger name="com.myapp"
level="DEBUG" additivity="false">
<AppenderRef ref="CONSOLE"/>
<AppenderRef ref="FILE"/>
<AppenderRef ref="ERROR_FILE"/>
</Logger>
<!-- Framework noise reduction -->
<Logger name="org.springframework" level="WARN"/>
<Logger name="org.hibernate.SQL" level="DEBUG"/>
<Logger name="org.hibernate.type" level="WARN"/>
<!-- Root logger -->
<Root level="INFO">
<AppenderRef ref="CONSOLE"/>
<AppenderRef ref="FILE"/>
<AppenderRef ref="ERROR_FILE"/>
</Root>
</Loggers>
</Configuration>Async Loggers
Log4j2's all-async mode uses the LMAX Disruptor ring buffer to decouple the application thread from I/O. Logging calls return immediately — typically in nanoseconds — while a background thread handles the actual I/O. This delivers the highest throughput of any Java logging implementation.
XML
<!-- pom.xml — add LMAX Disruptor for async loggers ────────────────── -->
<dependency>
<groupId>com.lmax</groupId>
<artifactId>disruptor</artifactId>
<version>3.4.4</version>
</dependency>
<!-- ── Option 1: All-async mode (fastest) ──────────────────────────── -->
<!-- Set system property BEFORE Log4j2 initialises -->
<!-- In application startup or JVM argument: -->
<!-- -DLog4jContextSelector=
org.apache.logging.log4j.core.async.AsyncLoggerContextSelector -->
// ── Set programmatically in main class ───────────────────────────────
public class Application {
static {
// Must be set before any Log4j2 class is loaded
System.setProperty("log4j2.contextSelector",
"org.apache.logging.log4j.core.async" +
".AsyncLoggerContextSelector");
}
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
<!-- ── Option 2: Async logger per logger (mixed mode) ─────────────── -->
<!-- src/main/resources/log4j2-spring.xml -->
<Loggers>
<!-- Async root — all loggers async by default -->
<AsyncRoot level="INFO">
<AppenderRef ref="FILE"/>
</AsyncRoot>
<!-- Or selectively async -->
<AsyncLogger name="com.myapp"
level="DEBUG" additivity="false">
<AppenderRef ref="CONSOLE"/>
<AppenderRef ref="FILE"/>
</AsyncLogger>
</Loggers>
# ── application.yml — tune async queue ───────────────────────────────
# Add to JVM args:
# -DLog4jContextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector
# -Dlog4j2.asyncQueueFullPolicy=Discard
# -Dlog4j2.discardThreshold=INFO
// ── Async performance characteristics ─────────────────────────────────
// Synchronous Logback: ~500ns per log call (under load)
// Log4j2 mixed async: ~250ns per log call
// Log4j2 all-async (Disruptor): ~25ns per log call
// → ~20x faster under high concurrencyJSON Layout for Structured Logging
Log4j2's JsonTemplateLayout produces structured JSON output without third-party dependencies. It supports custom templates, ECS (Elastic Common Schema), and GELF (Graylog Extended Log Format). Use it in production to enable structured log queries in Elasticsearch, OpenSearch, and Loki.
XML
<!-- pom.xml — JsonTemplateLayout is in log4j-layout-template-json -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-layout-template-json</artifactId>
</dependency>
<!-- ── JsonTemplateLayout in log4j2-spring.xml ─────────────────────── -->
<Appenders>
<!-- Default JSON template (Log4j2 standard) -->
<Console name="JSON_CONSOLE" target="SYSTEM_OUT">
<JsonTemplateLayout/>
</Console>
<!-- ECS (Elastic Common Schema) template -->
<Console name="ECS_CONSOLE" target="SYSTEM_OUT">
<JsonTemplateLayout
eventTemplateUri="classpath:EcsLayout.json">
<EventTemplateAdditionalField
key="service.name"
value="${spring:spring.application.name}"/>
<EventTemplateAdditionalField
key="service.environment"
value="${spring:spring.profiles.active:-default}"/>
</JsonTemplateLayout>
</Console>
<!-- Custom template file -->
<Console name="CUSTOM_JSON" target="SYSTEM_OUT">
<JsonTemplateLayout
eventTemplateUri="classpath:custom-log-template.json"/>
</Console>
</Appenders>
// ── custom-log-template.json ──────────────────────────────────────────
// {
// "timestamp": {"$resolver": "timestamp", "pattern": {"format": "ISO8601"}},
// "level": {"$resolver": "level", "field": "name"},
// "logger": {"$resolver": "logger", "field": "name"},
// "message": {"$resolver": "message", "stringified": true},
// "thread": {"$resolver": "thread", "field": "name"},
// "traceId": {"$resolver": "mdc", "key": "traceId"},
// "spanId": {"$resolver": "mdc", "key": "spanId"},
// "correlationId": {"$resolver": "mdc", "key": "correlationId"},
// "exception": {
// "$resolver": "exception",
// "field": "stackTrace",
// "stackTrace": {"stringified": true}
// }
// }
// ── Sample JSON output ────────────────────────────────────────────────
// {
// "timestamp": "2024-03-15T10:30:00.123Z",
// "level": "INFO",
// "logger": "com.myapp.service.OrderService",
// "message": "Order 42 created for user 1",
// "thread": "http-nio-8080-exec-1",
// "traceId": "abc123def456",
// "correlationId": "req-uuid-789"
// }Log4j2 vs Logback Comparison
Logback is the safer default — it is Spring Boot's choice, well-documented, and sufficient for most applications. Log4j2 is worth adopting when throughput is a concern, when Garbage-Free logging matters, or when the richer plugin system is needed. Both support the same SLF4J API so switching is a configuration-only change.
Java
// ── Feature comparison ───────────────────────────────────────────────
//
// Feature Logback Log4j2
// ─────────────────────────────────────────────────────────────────────
// Spring Boot default Yes No (must configure)
// Async logging AsyncAppender AsyncLogger (Disruptor)
// Async performance ~500ns/call ~25ns/call (all-async)
// Garbage-Free logging No Yes (reduces GC pauses)
// Configuration formats XML only XML, JSON, YAML, Props
// Configuration reload scan="true" monitorInterval
// JSON output logstash-encoder JsonTemplateLayout
// Plugin system Limited Rich (custom appenders)
// Profile-aware config <springProfile> Spring lookup plugin
// Memory footprint Lower Higher (Disruptor)
// Vulnerability history Minimal Log4Shell (2021, patched)
// Recommendation Default choice High-throughput APIs
// ── When to choose Log4j2 ─────────────────────────────────────────────
// - 10k+ requests/second with significant logging
// - Low-latency applications where GC pauses matter
// - Need for YAML/JSON/Properties configuration format
// - Custom appenders or lookup plugins
// ── When to stay with Logback ─────────────────────────────────────────
// - Standard web applications (most cases)
// - Prefer Spring Boot defaults and less configuration
// - Team is already familiar with Logback XML
// ── Migration: Logback → Log4j2 ──────────────────────────────────────
// 1. Swap dependencies (exclude starter-logging, add starter-log4j2)
// 2. Rename logback-spring.xml → log4j2-spring.xml
// 3. Translate XML syntax (Appenders/Loggers structure differs slightly)
// 4. Test: logging.level.* in application.yml works unchanged
// 5. All SLF4J Logger calls work unchanged — no code changes needed