Log4j2 offers significant advantages over Logback, particularly in terms of performance and asynchronous logging capabilities. While Spring Boot typically uses Logback by default, there are compelling reasons to consider Log4j2 instead, especially in high-concurrency scenarios where async logging can make a substantial difference in system performance.
Integrating Log4j2 with Spring Boot
To integrate Log4j2 with Spring Boot, you need to first exclude the default logging starter and add the Log4j2 starter:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<!-- Exclude the default logging configuration -->
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
<!-- Required for async logging support -->
<dependency>
<groupId>com.lmax</groupId>
<artifactId>disruptor</artifactId>
<version>3.4.4</version>
</dependency>
Note: Some Spring Boot starters may include the default logging dependency. Use your IDE's dependency viewer to check if spring-boot-starter-logging is included in your project and exclude it if necessary.
Configuring Log4j2 in Spring Boot
Specify the Log4j2 configuration file in your application.yml or application.properties:
logging:
config: classpath:log4j2.xml
The log4j2.xml file contains all the logging configuration. For detailed configuration options, refer to the official Log4j2 documentation: https://logging.apache.org/log4j/2.x/manual/configuration.html
Enabling Async Logging
The key to enabling async logging is configuring AsyncLoggers. You can configure three types of loggers: synchronous, asynchronous, and console. Here's an example of an async logger configuration:
<AsyncLogger name="com.example.service" additivity="false" includeLocation="true">
<appender-ref ref="FileAppenderInfo" />
<appender-ref ref="FileAppenderDebug" />
<appender-ref ref="FileAppenderWarn" />
<appender-ref ref="FileAppenderError" />
</AsyncLogger>
When configuring multiple appenders for a specific package or class, you must use a single AsyncLogger with multiple appender-ref elements. If you define multiple loggers for the same class, only one will be effective.
The additivity property doesn't always work as expected depending on the appender configuration. It's recommended to set includeLocation to false for better performance in production environments.
Separating Log Levels to Different Files Without Duplication
By default, higher-level log messages are output to all files, which can be convenient for debugging but creates large log files. To separate log levels into different files without duplication, configure your appenders with appropriate filters:
<RollingFile name="FileAppenderDebug" fileName="${log.path}/debug.log"
filePattern="${log.path}/archive/debug-%d{yyyy-MM-dd}-%i.log.gz">
<Filters>
<ThresholdFilter level="ERROR" onMatch="DENY" onMismatch="NEUTRAL"/>
<ThresholdFilter level="WARN" onMatch="DENY" onMismatch="NEUTRAL"/>
<ThresholdFilter level="INFO" onMatch="DENY" onMismatch="NEUTRAL"/>
<ThresholdFilter level="DEBUG" onMatch="ACCEPT" onMismatch="DENY"/>
</Filters>
<PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/>
<Policies>
<TimeBasedTriggeringPolicy/>
<SizeBasedTriggeringPolicy size="10MB"/>
</Policies>
<DefaultRolloverStrategy max="10"/>
</RollingFile>
<RollingFile name="FileAppenderInfo" fileName="${log.path}/info.log"
filePattern="${log.path}/archive/info-%d{yyyy-MM-dd}-%i.log.gz">
<Filters>
<ThresholdFilter level="ERROR" onMatch="DENY" onMismatch="NEUTRAL"/>
<ThresholdFilter level="WARN" onMatch="DENY" onMismatch="NEUTRAL"/>
<ThresholdFilter level="INFO" onMatch="ACCEPT" onMismatch="DENY"/>
</Filters>
<PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/>
<Policies>
<TimeBasedTriggeringPolicy/>
<SizeBasedTriggeringPolicy size="10MB"/>
</Policies>
<DefaultRolloverStrategy max="10"/>
</RollingFile>
The key to this configuration is the ThresholdFilter with NEUTRAL onMismatch values. This ensures that log messages are only processed by the appropriate filter for their level.
Console Output Configuraton
For development environments, you may want to configure console output:
<appenders>
<console name="Console" target="SYSTEM_OUT">
<ThresholdFilter level="DEBUG" onMatch="ACCEPT" onMismatch="DENY"/>
<PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/>
</console>
</appenders>
<loggers>
<root level="ALL">
<appender-ref ref="Console"/>
</root>
</loggers>
In Spring Boot, you can create environment-specific configuration files (e.g., log4j2-dev.xml) and specify them in application-dev.properties:
logging:
config: classpath:log4j2-dev.xml
Configuring Email Alerts
To configure email alerts, add the required dependency and SMTP appender:
<dependency>
<groupId>com.sun.mail</groupId>
<artifactId>javax.mail</artifactId>
<version>1.6.2</version>
</dependency>
<appenders>
<SMTP name="MailAppender" subject="Application Alert" to="admin@example.com" from="app@example.com"
smtpHost="smtp.example.com"
smtpPort="25"
smtpPassword="generated-password"
smtpProtocol="smtp"
smtpUsername="app@example.com"
smtpDebug="false"
bufferSize="50">
<ThresholdFilter level="WARN" onMatch="ACCEPT" onMismatch="DENY"/>
</SMTP>
<Async name="AsyncMail">
<appender-ref ref="MailAppender"/>
</Async>
</appenders>
<loggers>
<root level="ALL">
<appender-ref ref="Console"/>
<appender-ref ref="AsyncMail"/>
</root>
</loggers>
Note: If you encounter authentication errors, you may need to enable SMTP access for your email account and use the generated password rather than your original password.