Using Log4J 2 with Spring Boot

Using Log4J 2 with Spring Boot

14 Comments

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.
Log4J 2 SL4J Bridge

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.

Spring Framework 5
Click here to learn Spring Framework 5!

Maven Dependencies

To use Log4J 2 in a Spring Boot application, we need to add the required dependencies to the Maven POM. The required steps are:

    1. 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.

    1. 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>
. . .
    1. 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.

    1. 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.

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>guru.springframework</groupId>
  <artifactId>blogposts</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <packaging>jar</packaging>

  <name>Blog Posts</name>
  <description>Misc Blog Posts</description>

  <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>1.2.3.RELEASE</version>
    <relativePath/> <!-- lookup parent from repository -->
  </parent>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <start-class>guru.springframework.blog.BlogPostsApplication</start-class>
    <java.version>1.8</java.version>
  </properties>

  <dependencies>
    <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>
    
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-test</artifactId>
      <scope>test</scope>
    </dependency>
    <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>com.h2database</groupId>
      <artifactId>h2</artifactId>
      <scope>runtime</scope>
    </dependency>
    <dependency>
      <groupId>org.hibernate</groupId>
      <artifactId>hibernate-core</artifactId>
      <version>4.3.10.Final</version>
    </dependency>
  </dependencies>
  <build>
    <plugins>
      <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
      </plugin>
    </plugins>
  </build>

</project>

 

Logging Messages in a Spring Boot Application

We 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.

IndexController.java

package guru.springframework.blog.controllers;


import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class IndexController {
    private final Logger logger = LoggerFactory.getLogger(this.getClass());
    @RequestMapping("/")
    String index(){
        logger.debug("This is a debug message");
        logger.info("This is an info message");
        logger.warn("This is a warn message");
        logger.error("This is an error message");
        return "index";
    }
}

 

In the IndexController class above, we used the LoggerFactory and Logger classes 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

When you run the main class, INFO and higher level log messages of Spring Boot startup are logged to the console and the logs/spring-boot-logging.log file.
Spring Boot Startup Log Messages from Log4J 2

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.

Once the application starts, access it with the URL, http://localhost:8080. Spring Boot outputs INFO and higher level log messages of IndexController to the console and file.
Application Log Messages in Console and File from Log4J 2

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.

<?xml version="1.0" encoding="UTF-8"?>
<Configuration monitorInterval="60">
    <Properties>
        <Property name="log-path">applogs</Property>
    </Properties>
    <Appenders>
        <Console name="Console-Appender" target="SYSTEM_OUT">
            <PatternLayout>
                <pattern>
                    [%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %c{1} - %msg%n
                </pattern>>
            </PatternLayout>
        </Console>
        <File name="App-File-Appender" fileName="${log-path}/app_log.log" >
            <PatternLayout>
                <pattern>
                    [%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %c{1} - %msg%n
                </pattern>
            </PatternLayout>
        </File>
        <File name="SpringBoot-File-Appender" fileName="${log-path}/springboot_log.log" >
            <PatternLayout>
                <pattern>
                    [%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %c{1} - %msg%n
                </pattern>
            </PatternLayout>
        </File>
    </Appenders>
    <Loggers>
        <Logger name="org.springframework.web" level="info" additivity="false">
            <AppenderRef ref="SpringBoot-File-Appender"/>
            <AppenderRef ref="Console-Appender"/>
        </Logger>
        <Logger name="guru.springframework.blog.controllers" level="info" additivity="false">
            <AppenderRef ref="App-File-Appender"/>
            <AppenderRef ref="Console-Appender"/>
         </Logger>
        <Root>
            <AppenderRef ref="Console-Appender"/>
        </Root>
    </Loggers>
</Configuration>

 

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 INFO and higher level messages to the springboot_log.log file and the console.
  • Line 34 – Line 37: We configured the loggers of the guru.springframework.blog.controllers package and its sub packages to log INFO and higher level messages to the app_log.log file 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.

When you run and access the application now, Spring Boot loads the log42-spring.xml file and uses the configuration to log messages to the springboot_log.log file, app_log.log file, and console.
XML Logging Configuration Output

Summary

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.

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.

 

You can find the code of this post on Github

Spring Framework 5
Learn Spring Framework 5!

About jt

    You May Also Like

    14 comments on “Using Log4J 2 with Spring Boot

    1. April 7, 2016 at 11:20 am

      Hi,
      In console when I use application.properies or log4j2-spring.xml I have this error:

      “ERROR StatusLogger No log4j2 configuration file found. Using default configuration: logging only errors to the console.”

      It disappear only if I use log4j2.xml configuration file.

      How can I resolve this issue?

      Reply
    2. April 7, 2016 at 1:26 pm

      It is a known bug. The configuration in log4j2-spring.xml will work as expected.

      https://github.com/spring-projects/spring-boot/issues/4809

      You should be able to disable the message with the following system property:

      -Dorg.apache.logging.log4j.simplelog.StatusLogger.level=OFF

      Reply
      • April 7, 2016 at 3:19 pm

        Thank You Ximanta.
        I don’t even notice that it works, this error confused me.

        Reply
    3. October 24, 2016 at 7:21 am

      In log4j v2, you can use multiple configuration files, e.g.

      log4j.configurationFile=base.log4j2.xml,myapp.log4j2.xml

      How do I configure do this with spring boot?

      Reply
      • December 4, 2016 at 8:13 pm

        @Niels
        I am also trying to use spring boot with multiple configuration files for log4j2 and could not made it to work. Seems like Springboot looks for specific log4j2.xml (or -spring variant) and if it cannot find it (like in your example and in my case too) will load the one bundled with spring boot : org.springframework.boot.logging.log4j2.log4j2.xml.

        Reply
        • December 6, 2016 at 2:27 am

          We ended up moving to logback.

          Reply
    4. December 19, 2016 at 7:45 am

      There is a more simple way to configure maven dependencies – http://stackoverflow.com/a/27457106

      Reply
    5. January 24, 2017 at 2:37 am

      http://stackoverflow.com/questions/28349309/how-can-i-change-the-default-location-of-log4j2-xml-in-java-spring-boot

      Looks like logging.config property in application.properties cannot be used.

      Reply
    6. July 23, 2017 at 10:14 am

      How to configure log4j2 based on spring.profiles.active in spring boot? I have log4j2-dev.xml, log4j2-prod.xml.
      I tried with logging.config. But it is not working.

      Reply
    7. August 2, 2017 at 6:13 am

      I am trying to externalize something like this in SpringBootServletInitializer implementation main class. it does create log file but writes only the lines mentioned in this method and nothing else.

      is this correct way of externalizing the log4j2.xml with spring boot? am I doing anything wrong?

      public static void main(final String[] args) throws Exception {

      final SpringApplication application = new SpringApplication(MlmApplication.class);

      String configHome = System.getProperty(“CONFIG_HOME”);

      LoggerContext context = (LoggerContext) LogManager.getContext(false);
      File file = new File(configHome + “/log4j2.xml”);
      context.setConfigLocation(file.toURI());
      LOG.info(“log4j2.xml path is set to ” + file.getAbsolutePath());

      final Properties properties = new Properties();
      application.setBannerMode(Mode.CONSOLE);
      application.setDefaultProperties(properties);

      application.run();
      }

      Reply
    8. February 11, 2018 at 1:02 pm

      Hi! It’s a good idea to use policies of log4j2 such as SizeBasedTriggeringPolicy, TimeBasedTriggeringPolicy and also use strategies DefaultRolloverStrategy for example with RollingFile Loggers. The power of log4g2 is in such possibilities.

      Reply
    9. November 15, 2018 at 10:42 am

      Somewhat of a trivial question – where does log-path come from in the XML file?

      Reply
    10. January 12, 2023 at 3:56 am

      how to show logs on web page using log4j2 in spring boot, And merge two different logs of two different servers and show them on web page.

      Reply

    Leave a Reply

    Your email address will not be published. Required fields are marked *

    This site uses Akismet to reduce spam. Learn how your comment data is processed.