LearnAI ToolsCareerPractice BuildsPlayContact
Lesson 1620 min read

Logging Dependencies

Cover the default Logback logging starter, structured JSON logs with logstash-logback-encoder, and swapping to Log4j2 with the required exclusion.

Introduction

Every Spring Boot application logs something from the moment it starts, even if you never add a single logging dependency yourself — spring-boot-starter-web and every other starter pull in logging transitively. This lesson covers what is there by default, how to shape it into structured output, and how to replace it entirely if you need to.

What You Will Learn
  • What spring-boot-starter-logging provides by default and why it is already on your classpath.
  • How to configure output with logback-spring.xml.
  • How to produce structured JSON logs with logstash-logback-encoder.
  • How to swap to Log4j2, and why the default starter must be explicitly excluded first.

The Default: Logback

spring-boot-starter-logging is Spring Boot's default logging implementation, built on Logback, and it comes in transitively through every spring-boot-starter-* dependency — you do not add it yourself, and you will not typically see it listed explicitly in a pom.xml.

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Service
public class OrderService {
private static final Logger log = LoggerFactory.getLogger(OrderService.class);
public void placeOrder(String customerId, double amount) {
log.info("Placing order for customer={} amount={}", customerId, amount);
if (amount <= 0) {
log.warn("Rejected order with non-positive amount={}", amount);
throw new IllegalArgumentException("Amount must be positive");
}
}
}
Default Console Output

Click Run to see what this code prints.

Application code talks to SLF4J (the logging facade), and Logback is simply the implementation underneath actually formatting and writing those log lines — this separation is exactly what makes it possible to swap in Log4j2 later without touching a single log.info() call.

Configuring logback-spring.xml

Placing a logback-spring.xml file in src/main/resources lets you control log levels, output format, and destinations (console, rolling file, or both) without touching application code. The "-spring" suffix matters: it lets the file use Spring profile-specific configuration, unlike a plain logback.xml.

<configuration>
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} %-5level [%thread] %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>logs/application.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>logs/application.%d{yyyy-MM-dd}.log</fileNamePattern>
<maxHistory>30</maxHistory>
</rollingPolicy>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level [%thread] %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="CONSOLE" />
<appender-ref ref="FILE" />
</root>
<logger name="com.example.shop" level="DEBUG" />
</configuration>
Effect

Click Run to see what this code prints.

Structured JSON Logs

Plain-text log lines are easy to read on a laptop but hard for a log aggregation system (like the ELK stack or Datadog) to parse reliably. net.logstash.logback:logstash-logback-encoder formats each log line as a single JSON object instead, with fields the aggregator can index directly.

<dependency>
<groupId>net.logstash.logback</groupId>
<artifactId>logstash-logback-encoder</artifactId>
<version>7.4</version>
</dependency>
<configuration>
<appender name="JSON_CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="net.logstash.logback.encoder.LogstashEncoder" />
</appender>
<root level="INFO">
<appender-ref ref="JSON_CONSOLE" />
</root>
</configuration>
Resulting Log Line

Click Run to see what this code prints.

Swapping to Log4j2

Some teams standardize on Log4j2 across all their services instead of Logback. Because logging comes in transitively, switching requires two steps: excluding the default logging starter, and adding the Log4j2 starter in its place.

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</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>
Do Not Skip the Exclusion

Adding spring-boot-starter-log4j2 without excluding spring-boot-starter-logging first puts two competing SLF4J bindings on the classpath at once. The application will fail to start with a "Class path contains multiple SLF4J bindings" error.

Everything else — the log.info() calls scattered through the codebase, since they go through SLF4J — stays exactly the same. Only the configuration file changes from logback-spring.xml to log4j2-spring.xml.

Common Mistakes

Avoid These Mistakes
  • Adding a Log4j2 starter without excluding spring-boot-starter-logging first, causing a startup failure.
  • Naming the config file logback.xml instead of logback-spring.xml when profile-specific sections are needed — plain logback.xml loads too early for Spring profiles to apply.
  • Logging sensitive data (passwords, full card numbers, tokens) at INFO level where it ends up in aggregated, searchable logs.
  • Leaving the root logger at DEBUG in production, which floods log storage and slows the application under load.

Frequently Asked Questions

Not strictly — you could ship plain-text logs and parse them downstream — but emitting structured JSON at the source is far more reliable than trying to regex-parse free-form text later.

Yes — define two appenders with different encoders in logback-spring.xml (a plain PatternLayout for the console, LogstashEncoder for the file) and attach both to the root logger.

SLF4J is a facade — application code depends only on its API, so the concrete implementation underneath (Logback, Log4j2) can be swapped without touching a single logging call.

Summary

Logback arrives by default through every starter, logback-spring.xml is where you shape its output, logstash-logback-encoder gets you machine-parseable JSON, and swapping to Log4j2 only requires one careful exclusion. Next, we look at the dependencies that make local development itself faster.

Next Lesson →

DevTools & Developer Experience Dependencies