Spring Boot: Logging with SLF4J & Logback
Spring Boot uses SLF4J as the logging facade and Logback as the default implementation, included automatically via spring-boot-starter-logging. No extra dependency or XML config is needed to get started: Spring Boot pre-configures a sensible console output with colour-coded log levels.
Contents
Default Logging Setup
Out of the box, Spring Boot logs to the console at INFO
- Logging framework: Logback (via
)spring-boot-starter-logging - Facade: SLF4J. Your code always uses SLF4J; the implementation can be swapped.
- Default level:
for your application,INFO for most Spring framework classes.WARN
SLF4J Logger Usage
Get one logger per class and use {}
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
@Service
public class OrderService {
// Declare a logger per class — use the class itself as the logger name
private static final Logger log = LoggerFactory.getLogger(OrderService.class);
public Order placeOrder(OrderRequest request) {
log.debug("Placing order for customer: {}", request.getEmail());
Order order = orderRepository.save(new Order(request));
log.info("Order placed successfully — orderId={}, customerId={}",
order.getId(), request.getCustomerId());
return order;
}
public void processPayment(Long orderId) {
try {
paymentService.charge(orderId);
} catch (PaymentException e) {
log.error("Payment failed for orderId={}", orderId, e); // exception as last arg
}
}
}
{}Log Levels
| Level | Use for |
|---|---|
TRACE | Very fine-grained diagnostics: SQL parameters, loop iterations |
DEBUG | Development diagnostics: method entry/exit, variable values |
INFO | Normal application flow: startup, request received, order placed |
WARN | Unexpected but recoverable situations: retry, fallback used |
ERROR | Errors that require attention: exceptions, failed operations |
Levels are ordered: TRACE < DEBUG < INFO < WARN < ERRORINFOTRACEDEBUG
Configuring Log Levels in application.properties
The settings below can be placed in application.ymlapplication.properties
# Root log level
logging.level.root=WARN
# Your application packages
logging.level.com.example=DEBUG
logging.level.com.example.repository=TRACE
# Third-party libraries
logging.level.org.springframework.web=DEBUG
logging.level.org.hibernate.SQL=DEBUG
logging.level.org.hibernate.type.descriptor.sql=TRACE
# Write logs to a file
logging.file.name=logs/app.log
# Log pattern (console and file)
logging.pattern.console=%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
logging.pattern.file=%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
logback-spring.xml Customisation
Create src/main/resources/logback-spring.xmllogback-spring.xmllogback.xml<springProfile>
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<!-- Console appender -->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<!-- Rolling file appender — daily rotation, keep 30 days -->
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>logs/app.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>logs/app.%d{yyyy-MM-dd}.log.gz</fileNamePattern>
<maxHistory>30</maxHistory>
<totalSizeCap>1GB</totalSizeCap>
</rollingPolicy>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} [%X{traceId}] - %msg%n</pattern>
</encoder>
</appender>
<!-- Profile-specific configuration -->
<springProfile name="dev">
<root level="DEBUG">
<appender-ref ref="CONSOLE" />
</root>
</springProfile>
<springProfile name="prod">
<logger name="com.example" level="INFO"/>
<root level="WARN">
<appender-ref ref="FILE" />
</root>
</springProfile>
</configuration>
MDC for Contextual Logging
MDC (Mapped Diagnostic Context) lets you attach key-value pairs to the current thread that are automatically included in every log message. It is ideal for adding request IDs, user IDs, or trace IDs to all logs within a request scope.
import org.slf4j.MDC;
import jakarta.servlet.*;
import jakarta.servlet.http.*;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
@Component
public class MdcLoggingFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain chain) throws ServletException, IOException {
String requestId = Optional.ofNullable(request.getHeader("X-Request-ID"))
.orElse(UUID.randomUUID().toString());
try {
MDC.put("requestId", requestId);
MDC.put("method", request.getMethod());
MDC.put("uri", request.getRequestURI());
chain.doFilter(request, response);
} finally {
MDC.clear(); // always clean up to avoid thread-pool leakage
}
}
}
<!-- Include MDC values in log pattern with %X{key} -->
<pattern>%d{HH:mm:ss} [%thread] %-5level %logger{36} [reqId=%X{requestId}] - %msg%n</pattern>
MDC.clear()finally