Log4j2 is the latest release of the popular Logging Framework. In this tutorial we will learn how to integrate Log4j2 configuration file in an example Spring Boot web application.
Log4j2 is a new build of Apache Log4j which borrowed some ideas from the Logback framework featuring improved performance, support for multiple logger API (Log4j, SLF4J, Commons Logging and JUL), advanced filtering, low garbage collector impact and custom log levels.
In order to test Log4j2 in a Spring Boot application, let’s create a template Web application using the Spring Boot Initializr or the spring command line:
spring init -dweb demo-log4j2
From there, we need to refine a bit the configuration. First of all, we will be adding the starter for log4j2 in your pom.xml:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-log4j2</artifactId> </dependency>
There is one more step to do, as a matter of fact when using Spring Boot starters, Logback is used for logging by default. Spring Boot pre-configures Logback with patterns and ANSI colors to make the standard output more readable. As a proof of concept, let’s check the maven dependencies of our project just created:
[INFO] | +- org.springframework.boot:spring-boot-starter:jar:2.3.3.RELEASE:compile [INFO] | | +- org.springframework.boot:spring-boot:jar:2.3.3.RELEASE:compile [INFO] | | +- org.springframework.boot:spring-boot-autoconfigure:jar:2.3.3.RELEASE:compile [INFO] | | +- org.springframework.boot:spring-boot-starter-logging:jar:2.3.3.RELEASE:compile [INFO] | | | +- ch.qos.logback:logback-classic:jar:1.2.3:compile [INFO] | | | | \- ch.qos.logback:logback-core:jar:1.2.3:compile [INFO] | | | \- org.apache.logging.log4j:log4j-to-slf4j:jar:2.13.3:compile [INFO] | | +- jakarta.annotation:jakarta.annotation-api:jar:1.3.5:compile
So, in order to use a Logging framework other than LogBack, we have to exclude the default spring-boot-starter-logging from the top spring-boot-starter:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> <exclusions> <exclusion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-logging</artifactId> </exclusion> </exclusions> </dependency>
Now, if we check again the project dependencies, we will see that Log Back is not any more in the list:
[INFO] +- org.springframework.boot:spring-boot-starter:jar:2.3.3.RELEASE:compile [INFO] | +- org.springframework.boot:spring-boot:jar:2.3.3.RELEASE:compile [INFO] | | \- org.springframework:spring-context:jar:5.2.8.RELEASE:compile [INFO] | +- org.springframework.boot:spring-boot-autoconfigure:jar:2.3.3.RELEASE:compile [INFO] | +- jakarta.annotation:jakarta.annotation-api:jar:1.3.5:compile [INFO] | +- org.springframework:spring-core:jar:5.2.8.RELEASE:compile [INFO] | | \- org.springframework:spring-jcl:jar:5.2.8.RELEASE:compile
Then, let’s add a log4j2.xml configuration. The good news is that Spring Boot will automatically configure log4j2 once it finds it’s jar files in classpath. So, it is sufficient to add log4j2.xml or (log4j2.properties) in src/main/resources folder of your Maven project:
<Configuration status="DEBUG"> <Appenders> <Console name="LogToConsole" target="SYSTEM_OUT"> <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/> </Console> <File name="LogToFile" fileName="logs/application.log"> <PatternLayout> <Pattern>%d %p %c{1.} [%t] %m%n</Pattern> </PatternLayout> </File> </Appenders> <Loggers> <Logger name="com.example.demolog4j2" level="debug" additivity="false"> <AppenderRef ref="LogToFile"/> <AppenderRef ref="LogToConsole"/> </Logger> <Logger name="org.springframework.boot" level="error" additivity="false"> <AppenderRef ref="LogToConsole"/> </Logger> <Root level="error"> <AppenderRef ref="LogToFile"/> <AppenderRef ref="LogToConsole"/> </Root> </Loggers> </Configuration>
In order to use log4j2, we will add a minimalist Controller/Model/Repository example. Here is the Repository class, which logs when new Customer objects are created upon initialization:
import org.springframework.stereotype.Component; import javax.annotation.PostConstruct; import java.util.ArrayList; import java.util.List; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @Component public class CustomerRepository { List<Customer> customerList = new ArrayList<Customer>(); private static final Logger logger = LogManager.getLogger(CustomerRepository.class); @PostConstruct public void init() { Customer c1 = new Customer(1, "frank"); Customer c2 = new Customer(2, "john"); customerList.add(c1); logger.debug("Added Customer : {}", () -> c1); customerList.add(c2); logger.debug("Added Customer : {}", () -> c2); } public List<Customer> getData() { return customerList; } }
The full source code is available at the bottom of this article.
Now, let’s run the spring boot application:
$ mvn install spring-boot:run
By checking into the logs folder, the log4j Appender has created the fie “application.log”:
tail -f logs/application.log 2020-08-16 18:15:52,643 INFO c.e.d.DemoApplicationTests [main] No active profile set, falling back to default profiles: default 2020-08-16 18:15:53,354 DEBUG c.e.d.CustomerRepository [main] Added Customer : Customer{id=1, name='frank'} 2020-08-16 18:15:53,359 DEBUG c.e.d.CustomerRepository [main] Added Customer : Customer{id=2, name='john'} 2020-08-16 18:15:54,067 INFO c.e.d.DemoApplicationTests [main] Started DemoApplicationTests in 1.731 seconds (JVM running for 3.383) 2020-08-16 18:15:56,957 INFO c.e.d.DemoApplication [main] Starting DemoApplication on fedora with PID 16738 (/home/francesco/git/masterspringboot/log/demo-log4j2/target/classes started by francesco in /home/francesco/git/masterspringboot/log/demo-log4j2)
Configuring a specific Log4j2 version
In the example we have covered so far, we have used the spring-boot-starter-log4j2 to build our application. To have a strict control on the Log4j2 version we are deploying in our applications, it is however recommended to use Log4j2 BOM, which is also the best choice if you are using multiple components under the Log4j2 umbrella. To do that, add to your pom.xml the specific version of log4j-bom you want to use in your application:
<dependencyManagement> <dependencies> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-bom</artifactId> <version>2.13.3</version> <scope>import</scope> <type>pom</type> </dependency> </dependencies> </dependencyManagement>
Then, add the API needed in your application:
<dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-api</artifactId> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-core</artifactId> </dependency>
Using a RollingFile Appender
The RollingFileAppender is an OutputStreamAppender that writes to the File named in the fileName parameter and rolls the file over according the TriggeringPolicy and the RolloverPolicy.
Let’s see an example configuration:
<Configuration status="WARN" monitorInterval="30"> <Properties> <Property name="LOG_PATTERN">%d{yyyy-MM-dd'T'HH:mm:ss.SSSZ} %p %m%n</Property> </Properties> <Appenders> <Console name="Console" target="SYSTEM_OUT" follow="true"> <PatternLayout pattern="${LOG_PATTERN}" /> </Console> <RollingFile name="LogToFile" fileName="logs/application.log" filePattern="logs/application-%d{yyyy-MM-dd}-%i.log"> <PatternLayout pattern="${LOG_PATTERN}" /> <Policies> <TimeBasedTriggeringPolicy /> <SizeBasedTriggeringPolicy size="100 MB"/> </Policies> <DefaultRolloverStrategy max="1" /> </RollingFile> </Appenders> <Loggers> <Logger name="com.example.demolog4j2" additivity="false"> <AppenderRef ref="LogToFile" /> <AppenderRef ref="Console" /> </Logger> <Root level="debug"> <AppenderRef ref="Console" /> </Root> </Loggers> </Configuration>
The above log4j2.xml file will create up to 7 archives on the same day (1-7) to be stored in logs directory based on the current year and month, and will compress each archive using gzip.
Configuring Loggers to be asynchronous
To make all loggers asynchronous, Log4j-2.9 and higher require disruptor-3.3.4.jar or higher on the classpath. Prior to Log4j-2.9, disruptor-3.0.0.jar or higher was required.
Therefore, we will add to our pom.xml file:
<?xml version="1.0" encoding="UTF-8"?><project> <dependency> <groupId>com.lmax</groupId> <artifactId>disruptor</artifactId> <version>3.4.2</version> </dependency> </project>
To activate asynchronous logging, we also must set the system property log4j2.contextSelector to org.apache.logging.log4j.core.async.AsyncLoggerContextSelector:
mvn clean install -Dlog4j2.contextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector spring-boot:run
From the Spring Boot logs, you can verify that the AsyncContext has been activated, so now logs are running asynchronously:
2020-08-16 19:42:06,978 main DEBUG Registering MBean org.apache.logging.log4j2:type=AsyncContext@4b85612c 2020-08-16 19:42:06,979 main DEBUG Registering MBean org.apache.logging.log4j2:type=AsyncContext@4b85612c,component=AsyncLoggerRingBuffer 2020-08-16 19:42:06,979 main DEBUG Registering MBean org.apache.logging.log4j2:type=AsyncContext@4b85612c,component=StatusLogger 2020-08-16 19:42:06,980 main DEBUG Registering MBean org.apache.logging.log4j2:type=AsyncContext@4b85612c,component=ContextSelector 2020-08-16 19:42:06,982 main DEBUG Registering MBean org.apache.logging.log4j2:type=AsyncContext@4b85612c,component=Loggers,name=org.springframework.boot 2020-08-16 19:42:06,983 main DEBUG Registering MBean org.apache.logging.log4j2:type=AsyncContext@4b85612c,component=Loggers,name= 2020-08-16 19:42:06,984 main DEBUG Registering MBean org.apache.logging.log4j2:type=AsyncContext@4b85612c,component=Loggers,name=com.example.demolog4j2 2020-08-16 19:42:06,984 main DEBUG Registering MBean org.apache.logging.log4j2:type=AsyncContext@4b85612c,component=Appenders,name=LogToConsole 2020-08-16 19:42:06,985 main DEBUG Registering MBean org.apache.logging.log4j2:type=AsyncContext@4b85612c,component=Appenders,name=LogToFile
Source code for this tutorial: https://github.com/fmarchioni/masterspringboot/tree/master/log/demo-log4j2
References: https://logging.apache.org/log4j/2.0/index.html