1. Introduction

During this exercise we will switch the logging in Spring Boot from Logback (the default) to Log4j2.

2. Background info

Some definitions
  • SLF4J (Simple Logging Facade for Java) is the de-facto standard logging API used by Spring Boot. Your application code always logs through SLF4J.

  • The physical logging implementation is decoupled from SLF4J. This means you can swap the underlying logging framework without changing any of your application code.

  • Spring Boot supports several logging implementations:

    • Logback — the default, included via spring-boot-starter-logging

    • Log4j2 — a popular alternative with better async performance and more configuration options

    • java.util.logging — the JDK built-in logger (rarely used with Spring Boot)

Why switch to Log4j2?
  • Better performance in multi-threaded / high-throughput environments (especially with async logging)

  • Native support for YAML and JSON configuration files

  • Rich appender ecosystem (rolling files, databases, SMTP, etc.)

  • Dynamic reconfiguration without application restart (monitorInterval)

Spring Boot recommends naming your config file log4j2-spring.xml rather than log4j2.xml. The -spring variant allows Spring Boot to fully control log initialisation, including support for Spring profiles inside the logging config.

3. Exercise

During this exercise we will switch from Logback (default) to Log4j2.

3.1. Step 1: Update .gitignore

Amend the .gitignore to not add log files to the repository:

*.log
logs/

3.2. Step 2: Exclude Logback and add Log4j2

In your pom.xml, exclude the default logging starter from spring-boot-starter and add spring-boot-starter-log4j2:

<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-log4j2</artifactId>
</dependency>
You only need to exclude spring-boot-starter-logging once (from spring-boot-starter). Since all other starters transitively depend on spring-boot-starter, this single exclusion is sufficient. You do not need to add separate exclusions on spring-boot-starter-web, spring-boot-starter-actuator, etc.

3.3. Step 3: Add the Log4j2 configuration file

Create the file log4j2-spring.xml in src/main/resources:

<?xml version="1.0" encoding="UTF-8"?>
<Configuration monitorInterval="60">
    <Properties>
        <Property name="log-path">logs</Property>
        <Property name="archive">${log-path}/archive</Property>
        <Property name="LOG_PATTERN">[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %c{1} - %msg%n</Property>
    </Properties>

    <Appenders>
        <!-- Console output -->
        <Console name="Console-Appender" target="SYSTEM_OUT">
            <PatternLayout pattern="${LOG_PATTERN}"/>
        </Console>

        <!-- Simple file output -->
        <File name="File-Appender" fileName="${log-path}/carapp.log">
            <PatternLayout pattern="${LOG_PATTERN}"/>
        </File>

        <!-- Rolling file output (rotates daily or at 30 MB) -->
        <RollingFile name="RollingFile-Appender"
                     fileName="${log-path}/carapp-rolling.log"
                     filePattern="${archive}/carapp-rolling.%d{yyyy-MM-dd}.%i.log.gz">
            <PatternLayout pattern="${LOG_PATTERN}"/>
            <Policies>
                <TimeBasedTriggeringPolicy/>
                <SizeBasedTriggeringPolicy size="30 MB"/>
            </Policies>
            <DefaultRolloverStrategy max="30"/>
        </RollingFile>
    </Appenders>

    <Loggers>
        <!-- Root logger: INFO level to console and file -->
        <Root level="info">
            <AppenderRef ref="Console-Appender"/>
            <AppenderRef ref="File-Appender"/>
        </Root>

        <!-- Application logger: DEBUG level with rolling file -->
        <Logger name="com.acme.carapp" level="debug" additivity="false">
            <AppenderRef ref="Console-Appender"/>
            <AppenderRef ref="File-Appender"/>
            <AppenderRef ref="RollingFile-Appender"/>
        </Logger>
    </Loggers>
</Configuration>
Make sure the <Logger name="com.acme.carapp"> matches the base package of your application. If your package is different, adjust accordingly.

3.4. Step 4: Restart the application

Restart your application. You should notice:

  • The console log format has changed (it now uses the pattern from log4j2-spring.xml)

  • A logs/ directory has been created with carapp.log and carapp-rolling.log files

  • Your application-level logging (from com.acme.carapp) is at DEBUG level

3.5. Step 5: Validate that Log4j2 is active

You can verify that Log4j2 is being used (and Logback is gone) by checking your dependencies:

mvn dependency:tree | grep log

You should see spring-boot-starter-log4j2 and log4j-* artifacts, but no logback-classic or spring-boot-starter-logging.

You can also save the full dependency tree to a file for inspection: mvn dependency:tree > dependencies.log

4. Validating your logging

Add some test logging to your CarController or CarService:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

// Inside your class:
private static final Logger LOGGER = LoggerFactory.getLogger(CarController.class);

// Inside a method:
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");
Your Java code still uses org.slf4j.Logger and org.slf4j.LoggerFactory — the SLF4J API. The underlying implementation (now Log4j2) is completely transparent to your code. You do not need to change any imports.
  • The DEBUG message should appear in the console (because we configured com.acme.carapp at debug level)

  • All messages should also appear in logs/carapp.log

5. Bonus: Change log level via application.properties

You can also override log levels in application.properties without touching log4j2-spring.xml:

# Set the root log level
logging.level.root=WARN

# Set a specific package to TRACE
logging.level.com.acme.carapp=TRACE

This works the same way regardless of whether you use Logback or Log4j2 — Spring Boot abstracts it for you.

6. Takeaway

In this exercise, you learned how to switch from Logback to Log4j2 in a Spring Boot application. The key takeaways are that your application code does not change (it still uses SLF4J), and only the Maven dependencies and the configuration file are different.

7. Further reading

Spring Boot Logging - How To

https://docs.spring.io/spring-boot/how-to/logging.html

Logback and Log4j2 Extensions in Spring Boot

https://www.baeldung.com/spring-boot-logback-log4j2

Logging in Spring Boot

https://www.baeldung.com/spring-boot-logging