Similar to accompanying application development activities, such as unit testing and documentation, logging is an integral part of any enterprise application. And Spring Boot applications are no different. Through logging, you get to see what the Spring Boot application code is really doing during monitoring, troubleshooting, and debugging. Spring Boot makes using a logging framework effortless. Out of the box, Spring Boot is very easy to use with Logback. With Spring Boot’s default configuration, you can start writing out log messages using the Logback framework.
But what if you want to use Log4J 2? Naturally, Spring Boot has support for Log4J 2 and a number of other popular logging frameworks. If you are new to Log4J 2, I wrote an introductory post on Log4J 2 here.
Configuring Spring Boot to use Log4J 2 is easy to do. In this post, I will explain how to configure Spring Boot to use Log4J 2 over the Simple Logging Facade for Java (SL4J) logging façade.
What is the SL4J Façade?
Logging has been an important topic in the Java community for a long time. There is actually a fair amount of competition among the various logging frameworks. Determining which logging framework is the best has been a vigorously debated topic in the Java community. I’m not entering the “which logging framework is best” debate in this post. Each framework has its own merits and faults, and the landscape is always evolving.
When developing enterprise class applications using the Spring Framework, a common theme is to avoid tight coupling. For example, using Spring’s dependency injection, it’s common to build an application which can use different data sources. It’s fairly common to use H2 in memory database for development and MySQL for your production database. This is made easy via the DataSource interface, which is part of the standard Java API. H2 has an implementation of the DataSource interface. MySQL has an implementation of the DataSource interface. Through Spring configuration, we tell the IoC container which one we want to use at run time.
Logging in Java has been the wild west for a long time. Sun / Oracle did include a logging API as part of Java, but it never really caught on in the Java community. I’m honestly having a hard time remembering ever seeing it used.
While the Open Source community does offer a number of great logging solutions, as a developer, you don’t want to couple your code to a specific logging framework. This is where SLF4J steps in.
SL4J is a façade for commonly used logging frameworks, such as Java Util Logging, Log4J, Log4J 2, and Logback. As a developer, you write logging code against the SL4J API. At deployment time, you have the flexibility to plug-in your desired logging framework. This is made possible through an intermediate bridge/adapter layer, like this.
SL4J does not provide a complete logging solution. With SL4J, you cannot perform operations such as configuring appenders or setting logging levels. You perform such configurations through a configuration file of the logging framework in use. As the configuration file remains external to the application, no dependencies exist between your application code and the underlying logging framework.
Should you not provide a logging configuration to SL4J, it won’t complain. You’re logging simply becomes a no-op operation. Which is nice since it won’t unintentionally cause errors in your application. But also something to be aware of, if you’re not getting logging output.
To use Log4J 2 in a Spring Boot application, we need to add the required dependencies to the Maven POM. The required steps are:
- Use the latest Spring Boot version. At the time of writing this post, the Spring Boot version is 1.3.3.RELEASE.
. . . <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.3.3.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> . . .
Note: Spring Boot 1.2.4.RELEASE contains a bug, issue #3550. When you define a log level through application.properties, the bug changes the parent logger level, and in worst case the root logger level. Although the bug was fixed in 1.2.6.RELEASE, I suggest using the 1.2.8.RELEASE if you want to stick with 1.2.x.
- Add the SL4J dependencies.
. . . <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.19</version> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-slf4j-impl</artifactId> <version>2.5</version> </dependency> . . .
- Add the Log4J 2 dependencies.
. . . <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-api</artifactId> <version>2.5</version> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-core</artifactId> <version>2.5</version> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-jcl</artifactId> <version>2.5</version> </dependency> . . .
In the configuration code above, the
log4j-jcl artifact is Apache Log4J Commons Logging Bridge. It is required if you intend to route Spring Boot application.properties logging configuration to a Log4J 2 file appender.
- Exclude the built-in Logback dependency. This is necessary as Spring Boot will pick and use Logback if present in the classpath.
. . . <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> <exclusions> <exclusion> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> </exclusion> </exclusions> </dependency> . . .
The complete Maven POM is this.
4.0.0 guru.springframework spring-boot-web 0.0.1-SNAPSHOT jar Spring Boot Web Application Spring Boot Web Application org.springframework.boot spring-boot-starter-parent 1.3.3.RELEASE UTF-8 1.8 org.slf4j slf4j-api 1.7.19 org.apache.logging.log4j log4j-slf4j-impl 2.5 org.apache.logging.log4j log4j-api 2.5 org.apache.logging.log4j log4j-core 2.5 org.apache.logging.log4j log4j-jcl 2.5 org.springframework.boot spring-boot-starter-security ch.qos.logback logback-classic org.springframework.boot spring-boot-starter-thymeleaf org.springframework.boot spring-boot-starter-web org.webjars bootstrap 3.3.4 org.webjars jquery 2.1.4 com.h2database h2 runtime org.springframework.boot spring-boot-starter-test test org.springframework.boot spring-boot-maven-plugin
Logging Messages in a Spring Boot ApplicationWe will write log messages in the controller class of the Spring Boot application. As I have mentioned earlier, we will write log messages against the SL4J API. The code of the controller class is this.
IndexControllerclass above, we used the
Loggerclasses of SL4J to log debug, info, warn, and error messages.
Configuring Logging in application.properties
In a Spring Boot application, you can externalize configuration to work with the same application code in different environments. The application.properties file is one of the many ways to externalize configuration. Let's use it to externalize logging configuration.
In the default structure of a Spring Boot web application, you can locate the application.properties file under the Resources directory. In the application.properties file, you can define log levels of Spring Boot, application loggers, Hibernate, Thymeleaf, and lots more. You can also define a log file to write log messages in addition to the console. An example of an application.properties file with logging configuration is this.logging.level.org.springframework.web=INFO logging.level.guru.springframework.blogs.controllers=INFO logging.level.org.hibernate=ERROR logging.file=logs/spring-boot-logging.log log4j.logger.org.thymeleaf=DEBUG
Note: There is also a logging.path property to specify a path for a logging file. If you use it, Spring Boot creates a spring.log file in the specified path. However, you cannot specify both the logging.file and logging.path properties together. If specified together, Spring Boot will ignore both.
Using a Log4J 2 Configuration File
Log4J 2 configuration through application.properties file might be sufficient for simple applications. But, enterprise applications have far more complex logging requirements. Log4J 2 supports advanced logging configurations through configuration files, such as properties, XML, JSON, and YAML. I have explained how to configure Log4J 2 using XML here.
In a Spring Boot application, you can specify a Log4J 2 XML configuration file as log4j2.xml or log4j2-spring.xml in the project classpath. The Spring Boot team however recommends using the -spring variant for your logging configuration. That is log4j2-spring.xml rather than log4j2.xml. If you use the standard log4j2.xml configuration, Spring Boot might not be able to completely control log initialization.
The code of the log4j2-spring.xml file, is this.
In the configuration code above:
- Line 6 - Line 28: We defined a console appender and two file appenders.
- Line 30 - Line 33: We configured Spring Boot logging to log
INFOand higher level messages to the
springboot_log.logfile and the console.
- Line 34 - Line 37: We configured the loggers of the
guru.springframework.blog.controllerspackage and its sub packages to log
INFOand higher level messages to the
app_log.logfile and the console.
Note: Spring Boot expects the log4j2-spring.xml configuration file to be on the classpath. However, you can store it in a different location and point to it using the logging.config property in application.properties.
Earlier in this post, I mentioned about the Log4J 2 supported configurations. I have written the following posts for each of the configuration which also apply to Spring Boot applications.
- Log4J 2 Configuration: Using Properties File
- Log4J 2 Configuration: Using XML
- Log4J 2 Configuration: Using JSON
- Log4J 2 Configuration: Using YAML
Which configuration to use is largely a matter of personal preferences and your logging needs. If your needs are fairly simple, often using the properties file is all you'll need. But if you need to access more advanced logging features, you'll need to use XML, JSON, or YAML. XML is structured and IDE friendly. But JSON/YAML are more fun to write in.
When it comes to adding logging statements to your code, there are always questions, such as “What to log and what not to?”, “How much to log?”, and so on. There are no concrete answers. You should resist the tendency to log everything because logging means more code and that comes with overhead. Also the more you log, the more difficult it becomes to locate specific log messages when you later need them. Instead, write logs as demonstrated by specific, verifiable needs. One valuable resource that I suggest referring to is The Art of Logging. Although the article is an accompaniment to the Simple Logging Facade (SLF) framework, the recommended best practices apply to all logging frameworks, including Log4J 2.