Spring Boot - Logging

spring-boot

All the Spring Boot starters depend on spring-boot-starter-logging which uses Logback by default. If we want to use log4j, we need to exclude spring-boot-starter-logging and add spring-boot-starter-log4j2 as a dependency:

<!-- Exclude Spring Boot's Default Logging -->
<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>

<!-- Add Log4j2 Dependency -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>

Spring Boot automatically configures Log4j if it finds a file named log4j2.xml or log4j2.json or log4j2.yaml in the classpath.

In this article, we’ll configure log4j 2 using XML. Create a new file log4j2.xml inside src/main/resources directory, and add the following configuration to it:

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN" monitorInterval="30">
    <Properties>
        <Property name="LOG_PATTERN">
            %d{yyyy-MM-dd HH:mm:ss.SSS} %5p ${hostName} --- [%15.15t] %-40.40c{1.} : %m%n%ex
        </Property>
    </Properties>
    <Appenders>
        <Console name="ConsoleAppender" target="SYSTEM_OUT" follow="true">
            <PatternLayout pattern="${LOG_PATTERN}"/>
        </Console>
    </Appenders>
    <Loggers>
        <Logger name="com.example.log4j2demo" level="debug" additivity="false">
            <AppenderRef ref="ConsoleAppender" />
        </Logger>

        <Root level="info">
            <AppenderRef ref="ConsoleAppender" />
        </Root>
    </Loggers>
</Configuration>

The above configuration defines a simple ConsoleAppender and declares two loggers - an application specific logger and the the root logger.

To log messages:

package com.example.log4j2demo;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Log4j2DemoApplication implements ApplicationRunner {
    private static final Logger logger = LogManager.getLogger(Log4j2DemoApplication.class);

    public static void main(String[] args) {
        SpringApplication.run(Log4j2DemoApplication.class, args);
    }

    @Override
    public void run(ApplicationArguments applicationArguments) throws Exception {
        logger.debug("Debugging log");
        logger.info("Info log");
        logger.warn("Hey, This is a warning!");
        logger.error("Oops! We have an Error. OK");
        logger.fatal("Damn! Fatal error. Please fix me.");
    }
}

How can we use a Rolling File Appender?

If you want your logs to be written to a file, then you can use a RollingFile appender. RollingFile appender creates a new file whenever the log file reaches a certain threshold specified by the triggering policy. It stores the old log files with names matching the pattern specified by the filePattern parameter:

<!-- Rolling File Appender -->
<RollingFile name="FileAppender" fileName="logs/log4j2-demo.log" 
             filePattern="logs/log4j2-demo-%d{yyyy-MM-dd}-%i.log">
    <PatternLayout>
        <Pattern>${LOG_PATTERN}</Pattern>
    </PatternLayout>
    <Policies>
        <SizeBasedTriggeringPolicy size="10MB" />
    </Policies>
    <DefaultRolloverStrategy max="10"/>
</RollingFile>

In the above RollingFile configuration, I’ve specified a SizeBasedTriggeringPolicy which will roll files over when the size reaches 10MB. The <DefaultRollOverStrategy max="10" /> element specifies the maximum number of log files that will be kept.

We can add the above RollingFile configuration to the <Appenders> section inside log4j2.xml, and configure your logger to use it like so:

<Root level="info">
    <AppenderRef ref="ConsoleAppender"/>
    <AppenderRef ref="FileAppender"/>
</Root>

How can we use an SMTP Appender?

SMTP appender is very useful in production systems when you want to be notified about any errors in your application via email. You can configure an SMTP appender to send ERROR emails to you using an SMTP server like so:

<!-- SMTP Appender -->
<SMTP name="MailAppender"
      subject="Log4j2 Demo [PROD]"
      to="youremail@example.com"
      from="log4j2-demo-alerts@example.com"
      smtpHost="yourSMTPHost"
      smtpPort="587"
      smtpUsername="yourSMTPUsername"
      smtpPassword="yourSMTPPassword"
      bufferSize="1">
    <ThresholdFilter level="ERROR" onMatch="ACCEPT" onMismatch="DENY"/>
    <PatternLayout>
        <Pattern>${LOG_PATTERN}</Pattern>
    </PatternLayout>
</SMTP>

Note that, for SMTP appender to work, you need to include spring-boot-starter-mail dependency to your pom.xml file:

<!-- Needed for SMTP appender -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-mail</artifactId>
</dependency>

How can we use the Asychronous Logging feature of Log4J with Spring Boot?

Log4j2 Supports Async loggers. These loggers provide a drastic improvement in performance compared to their synchronous counterparts. Async Loggers internally use a library called Disruptor for asynchronous logging. We need to include Disruptor dependency for using async loggers. Add the following to your pom.xml file:

<!-- Needed for Async Logging with Log4j 2 -->
<dependency>
    <groupId>com.lmax</groupId>
    <artifactId>disruptor</artifactId>
    <version>3.3.6</version>
</dependency>

Now you can either make all Loggers asynchronous, or create a mix of sync and async loggers. Making all loggers asynchronous is very easy. You just need to set the SystemProperty Log4jContextSelector to org.apache.logging.log4j.core.async.AsyncLoggerContextSelector. You can do that by adding the System Property at runtime like so:

mvn spring-boot:run -DLog4jContextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector

or, when using a packaged jar:

mvn clean package
java -jar target/log4j2-demo-0.0.1-SNAPSHOT.jar -DLog4jContextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector

You can also use a mix of sync and async loggers with Log4j 2 using the <AsyncLogger> configuration element. In the following example, all the application specific logs will be asynchronous, and other root logs will be synchronous:

<Loggers>
    <AsyncLogger name="com.example.log4j2demo" level="debug" additivity="false">
        <AppenderRef ref="ConsoleAppender" />
        <AppenderRef ref="FileAppender" />
    </AsyncLogger>

    <Root level="info">
        <AppenderRef ref="ConsoleAppender" />
        <AppenderRef ref="FileAppender" />
    </Root>
</Loggers>

You don’t need to add any SystemProperty when you’re using <AsyncLogger> element. Just add the above loggers to your log4j2.xml file and you’re ready to go.

Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License