Using Logback and SLF4J for Logging in Spring Boot
This article explains why Logback is preferred over Log4j, shows how to add the necessary Maven dependencies, describes default and custom configurations via logback‑spring.xml, details logger, appender, layout, rolling policies and filters, and provides best practices for efficient logging in Spring Boot applications.
Introduction – In modern Java projects a logging system is essential. Log4j and Logback are popular frameworks, both created by the same author, with Logback being the successor to Log4j. SLF4J serves as a façade that defines common logging APIs, while Logback and Log4j provide concrete implementations.
Why use Logback – Logback is more efficient, works in many environments, natively supports SLF4J, offers flexible customization, and is the default logging framework bundled with Spring Boot.
Adding dependencies
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</dependency>Spring Boot already includes this starter, so you normally only need to add the web starter:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>Default configuration – By default Spring Boot logs to the console. To write to a file you can set logging.file or logging.path in application.properties . These two properties are mutually exclusive.
logging.file=my.log # absolute or relative path
logging.path=/var/log # creates spring.log in the directory
logging.level.com.example=DEBUG
logging.pattern.console=%d{HH:mm:ss.SSS} %-5level %logger{36} - %msg%nlogback‑spring.xml – Spring Boot recommends using a file whose name ends with -spring (e.g., logback-spring.xml ) and placing it under src/main/resources . This file allows full access to Spring’s profile support.
Core concepts
Logger – the entry point for logging calls.
Appender – the destination (console, file, etc.).
Layout/Encoder – defines the output format.
Basic XML example:
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<root level="DEBUG">
<appender-ref ref="STDOUT"/>
</root>
</configuration>The logger element can inherit its level from its parent unless a specific level is set. Examples of level inheritance are shown in the original article.
Appender types
ConsoleAppender – writes to the console.
RollingFileAppender – writes to a file and rolls the file based on time, size, or both.
Rolling policies such as SizeAndTimeBasedRollingPolicy allow you to keep logs for a configurable period (e.g., 30 days) and limit total size (e.g., 20 GB).
<appender name="info_log" class="ch.qos.logback.core.rolling.RollingFileAppender">
<File>logs/project_info.log</File>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>logs/project_info.%d.%i.log</fileNamePattern>
<maxHistory>30</maxHistory>
<totalSizeCap>20GB</totalSizeCap>
<maxFileSize>10MB</maxFileSize>
</rollingPolicy>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level --- [%15.15(%thread)] %cyan(%-40.40(%logger{40})) : %msg%n</pattern>
<charset>UTF-8</charset>
</encoder>
</appender>Filters – Two common filters are LevelFilter (matches a specific level) and ThresholdFilter (blocks events below a threshold). Example configurations are provided in the source.
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>INFO</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<encoder>
<pattern>%-4relative [%thread] %-5level %logger{30} - %msg%n</pattern>
</encoder>
</appender>Best practices for logging statements
Avoid constructing log messages when the log level is disabled. Use if (logger.isDebugEnabled()) { … } or the placeholder syntax.
Prefer placeholders: logger.debug("The entry is {}", entry); . The message is formatted only when the level is enabled.
When logging exceptions, include the throwable as a separate argument to capture the stack trace: logger.error("Application error: {}", e.getMessage(), e); .
These techniques can reduce unnecessary string concatenation and improve performance, especially in high‑throughput services.
Finally, the article provides links to additional resources such as a 7701‑page interview question PDF and other technical tutorials.
Architect's Tech Stack
Java backend, microservices, distributed systems, containerized programming, and more.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.