Logging — SLF4J, Logback, Log4j2, MDC, Structured Logging, and Log Levels
In this tutorial, you will learn about Logging. We cover key concepts, practical examples, and best practices to help you master this topic.
Java logging frameworks capture runtime information for debugging, monitoring, and auditing, with SLF4J as the standard abstraction layer. Logging is the primary mechanism for understanding what your application does in production — without it, diagnosing failures requires reproducing the issue locally.
What You'll Learn
- SLF4J: the logging facade
- Logback: the most popular implementation
- Log4j2: high-performance alternative
- MDC: mapped diagnostic context for request tracing
- Structured logging with JSON
Why It Matters
Good logging practices reduce mean-time-to-resolution (MTTR) for production incidents. Poor logging — no context, wrong level, too verbose — makes debugging harder. Understanding logging frameworks helps you choose and configure the right tools.
Real-World Use
Every application logs. Spring Boot uses Logback by default. Log aggregation tools (ELK, Splunk, Datadog) parse structured logs.
SLF4J — The Logging Facade
SLF4J is a facade — it provides a uniform API over multiple logging implementations:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class UserService {
private static final Logger log = LoggerFactory.getLogger(UserService.class);
public void createUser(String name) {
log.info("Creating user: {}", name);
// SLF4J parameterized — avoids string concatenation
}
}
Log Levels
log.trace("Detailed debugging — not used in production");
log.debug("Debug information — enabled in dev/test");
log.info("Notable events — user creation, login");
log.warn("Potentially harmful situations — low disk, deprecated API");
log.error("Errors — exceptions, failures that need investigation");
Parameterized Logging
Always use {} placeholders instead of string concatenation:
// GOOD — lazy evaluation
log.info("User {} logged in from {}", user.getId(), ipAddress);
// BAD — string concatenation happens even if log level is disabled
log.info("User " + user.getId() + " logged in from " + ipAddress);
Logback
Logback is the default SLF4J implementation in Spring Boot. Configuration goes in logback-spring.xml or logback.xml:
<configuration>
<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>
<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</fileNamePattern>
<maxHistory>30</maxHistory>
</rollingPolicy>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="FILE"/>
</root>
<logger name="com.example" level="DEBUG"/>
</configuration>
Log4j2
Log4j2 is a high-performance alternative using log4j2.xml:
<Configuration status="WARN">
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{HH:mm:ss.SSS} %-5level %logger{36} - %msg%n"/>
</Console>
<RollingFile name="File" fileName="logs/app.log"
filePattern="logs/app-%d{yyyy-MM-dd}.log.gz">
<PatternLayout pattern="%d %p %c{1.} [%t] %m%n"/>
<Policies>
<TimeBasedTriggeringPolicy/>
</Policies>
</RollingFile>
</Appenders>
<Loggers>
<Root level="INFO">
<AppenderRef ref="Console"/>
<AppenderRef ref="File"/>
</Root>
<Logger name="com.example" level="DEBUG"/>
</Loggers>
</Configuration>
MDC — Mapped Diagnostic Context
MDC stores contextual information per-thread, which is included in log output:
import org.slf4j.MDC;
public class RequestFilter {
public void handleRequest(String requestId, String userId) {
MDC.put("requestId", requestId);
MDC.put("userId", userId);
try {
// All logs in this thread include requestId and userId
log.info("Processing request");
} finally {
MDC.clear(); // Always clean up
}
}
}
Configure the pattern to include MDC values:
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} [%X{requestId}] - %msg%n</pattern>
Structured Logging
Structured logging outputs machine-parseable JSON instead of plain text:
<appender name="JSON" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="net.logstash.logback.encoder.LogstashEncoder"/>
</appender>
Maven dependency:
<dependency>
<groupId>net.logstash.logback</groupId>
<artifactId>logstash-logback-encoder</artifactId>
<version>7.4</version>
</dependency>
JSON output example:
{"@timestamp":"2026-06-28T14:30:00.000+00:00","level":"INFO","logger":"com.example.UserService","message":"Creating user: Alice","requestId":"abc-123"}
Common Mistakes
- Using
System.out.println()in production. Logging frameworks provide levels, configuration, and file rotation. Never useSystem.outfor logging. - String concatenation in log messages.
log.info("Value: " + x)always constructs the string, even if INFO is disabled. Uselog.info("Value: {}", x). - Logging exceptions without stack trace.
log.error("Error: " + e.getMessage())loses the stack trace. Always pass the exception as the last argument:log.error("Failed to Process", e). - Too much logging at the wrong level. Use DEBUG for detailed diagnostics, INFO for important events, WARN for potential problems, ERROR for failures.
- Not cleaning up MDC. MDC values persist across requests in thread pools. Always clear MDC in a finally block.
Practice Questions
1. What is the difference between SLF4J and Logback?
SLF4J is a facade (interface). Logback is an implementation. You write code against SLF4J and can swap implementations without changing code.
2. Why use parameterized logging ({}) instead of string concatenation?
String concatenation executes even when the log level is disabled. Parameterized logging evaluates only when the level is enabled.
3. What is MDC used for?
MDC stores per-thread contextual information (request ID, user ID) that is included in every log message from that thread.
4. What is structured logging?
Outputting logs as JSON instead of plain text, making them machine-parseable by log aggregation tools (ELK, Datadog).
5. What are the five SLF4J log levels?
TRACE, DEBUG, INFO, WARN, ERROR.
Challenge Question:
Design a logging Strategy for a microservice that handles HTTP requests. Include: request ID in every log line (via MDC), structured JSON output, different log levels for different packages, and a rolling file appender with archival (30 days). Configure DEBUG for your code, INFO for framework code, and WARN for third-party libraries.
FAQ
{{< faq "How do I configure different log levels for different packages?" "Add package-specific loggers: <logger name="org.springframework" level="WARN"/> leaves Spring at WARN while your code logs at DEBUG." >}}
Mini Project
Create a logging configuration project:
- Set up
logback-spring.xmlwith console and rolling file appenders - Configure MDC to include
requestIdanduserId - Add structured JSON logging with Logstash encoder
- Write a
LoggingDemoclass that logs messages at all levels - Demonstrate MDC by creating a
RequestContextfilter that sets MDC values - Show that
log.info("Hello {}")is faster thanlog.info("Hello " + name) - Configure different log levels for different packages
- Test that changing
logback.xmlreloads without restart (auto-scan)
What's Next
Logging is essential for observing behavior. Now it's time for concurrency — Lesson 51 introduces threads and Runnable, the Thread class, Runnable and Callable interfaces, thread states, and daemon threads.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro