Foundation · Observe
SLF4J, Logback & Log Levels
Meet the logging facade every Spring class already uses, pick the right level per event, and stop paying for string building nobody reads.
Primary source: Spring Boot 3.5 Reference: Logging
Lesson 1 of 2
1d streakThe Architectural Question
Println spam has no levels, no timestamps beyond chance, and cannot be filtered or shipped—so real signals drown and noisy files block nothing.
What this guide proves
- Call SLF4J loggers with parameterized messages
- Map events to ERROR, WARN, INFO, and DEBUG consistently
- Configure level per package in application.yml
The SLF4J Logging Facade & Why System.out is Forbidden
Logging in production enterprise applications is not simply about printing messages to a terminal; it is about emitting structured, filterable, thread-safe operational telemetry. Using System.out.println or System.err.println in enterprise Java is considered a critical code-quality defect for multiple reasons.
Architectural Boundary · Do Not Confuse
System.out.println: Synchronous blocking standard output. Synchronizes on PrintStream monitor (severe thread contention). No timestamps, no thread IDs, no log levels, and cannot be dynamically silenced in production.
SLF4J + Logback: leveled filtering (DEBUG/INFO/ERROR) with configurable appenders. Console and file appenders are synchronous unless an async appender is explicitly configured. Enriched with MDC context, thread names, timestamps, and routing to files, syslog, or JSON streams.
SLF4J (Simple Logging Facade for Java) acts as an abstraction layer (an API) decoupling application code from the underlying logging engine (Logback in Spring Boot defaults).
package com.example.order;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
@Service
public class OrderService {
// Canonical logger declaration:
private static final Logger log = LoggerFactory.getLogger(OrderService.class);
public void processOrder(String orderId, long amountCents) {
log.info("Processing order id={} amountCents={}", orderId, amountCents);
try {
// business logic
} catch (Exception ex) {
log.error("Failed to process order id={}: {}", orderId, ex.getMessage(), ex);
}
}
}The Static Logger Pattern
private static final Logger log = LoggerFactory.getLogger(TargetClass.class);. Making the logger static avoids allocating a new Logger reference for every instance created, saving memory across millions of domain objects.Active Recall Checkpoint
What happens if you pass an Exception as the final argument in log.error("Failed: {}", ex.getMessage(), ex)?
Hint: How does SLF4J treat a Throwable when passed as the last parameter?
Show answer & explanation
SLF4J recognizes Throwable as a special argument and prints the full stack trace automatically, even if there is no corresponding {} placeholder for it.
Log Level Hierarchy & Parameterized Message Formatting
SLF4J defines five standard log levels ordered by increasing severity: TRACE < DEBUG < INFO < WARN < ERROR. Setting a log level instructs the logger to emit messages at that severity and above while discarding lower-severity entries.
| Log Level | Intended Audience & Purpose | Production Recommendation |
|---|---|---|
| TRACE | Deep diagnostic tracing (method entry/exit, raw wire bytes, ASM instructions) | Disabled in production; enabled only for targeted troubleshooting |
| DEBUG | Developer troubleshooting (internal state transitions, query parameters, branch decisions) | Disabled in production; enabled per-package when investigating staging issues |
| INFO | Business milestones (service startup, payment completed, batch job finished, order placed) | Default production level for application packages |
| WARN | Unexpected but recoverable conditions (slow external response, fallback cache used, rate limit approaching) | Always enabled in production; triggers proactive monitoring alerts |
| ERROR | Critical failures requiring developer or ops intervention (unhandled exceptions, database outage, data corruption) | Always enabled in production; triggers pager alerts and Sentry issues |
The String Concatenation Performance Bug
log.debug("User " + user.getId() + " data: " + user.toHeavyString());. Even if DEBUG logging is completely disabled, the JVM still evaluates the string concatenation and calls toHeavyString(), wasting CPU and generating garbage collector pressure. Always use parameterized placeholders: log.debug("User {} data: {}", user.getId(), user);.Active Recall Checkpoint
When should you wrap a log statement with if (log.isDebugEnabled())?
Hint: Think about when parameterized placeholders {} are not enough.
Show answer & explanation
Use if (log.isDebugEnabled()) when constructing the log arguments themselves is computationally expensive (such as serializing a large JSON payload or querying an in-memory graph) so the calculation is skipped when DEBUG is off.
Configuring Logback in Spring Boot: application.yml vs logback-spring.xml
Spring Boot provides seamless logging configuration directly inside application.yml for basic level overrides, file logging, and console formatting. For advanced enterprise setups (rolling file policies, custom appenders, syslog), use logback-spring.xml.
logging:
level:
root: INFO
org.springframework.web: WARN
org.hibernate.SQL: DEBUG # Show SQL queries
org.hibernate.orm.jdbc.bind: TRACE # Show SQL bound parameters
com.example.order: DEBUG
pattern:
console: "%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(%5p) %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} : %m%n"
file:
name: "/var/log/order-service/application.log"Why name the file logback-spring.xml instead of logback.xml? If you use logback.xml, Logback initializes before Spring Boot loads its Environment. By using logback-spring.xml, Spring Boot controls Logback initialization, allowing you to use <springProfile name="prod"> tags and read properties via <springProperty scope="context" name="appName" source="spring.application.name"/>.
Active Recall Checkpoint
Why should you avoid naming your configuration file logback.xml in a Spring Boot application?
Hint: Consider whether Spring Boot environment features and profile tags will work.
Show answer & explanation
Standard logback.xml loads too early in the JVM lifecycle before Spring Boot's environment and profile processing are ready. Using logback-spring.xml allows you to leverage <springProfile> and <springProperty> tags.
Deliberate Failure Lab: Missing Logs & Logging Loop Explosions
A common developer panic in staging or production is: "I added logging, but nothing shows up in the console or log file!" Check the logger level, the effective root and package configuration, the active appender, and the deployment sink before changing application code.
@GetMapping("/pay")
public String pay() {
log.debug("Initiating payment calculation"); // If root is INFO and package level is unset, this is silently discarded!
return "ok";
}Diagnostic Verification: Dynamic Level Changes at Runtime
/actuator/loggers/com.example.order with {"configuredLevel": "DEBUG"}.Active Recall Checkpoint
How can you temporarily enable DEBUG logging for a single package in a running production Spring Boot app without a restart?
Hint: Which Actuator endpoint manages logger levels?
Show answer & explanation
Send an HTTP POST request to /actuator/loggers/<package-name> with body {"configuredLevel": "DEBUG"}. The change takes effect immediately in memory.
Working Implementation
Code Walkthrough
Convert System.out debugging into leveled logging and prove DEBUG disappears when prod sets info.
package com.example.logging;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
@Service
public class LeveledLoggingLab {
private static final Logger log = LoggerFactory.getLogger(LeveledLoggingLab.class);
public void processPayment(String orderId, long amountCents) {
// TRACE: High-volume wire-level detail
log.trace("Entering processPayment for orderId={}", orderId);
// DEBUG: Internal state transitions and branch evaluations
log.debug("Evaluating fraud check rules for orderId={} amount={}", orderId, amountCents);
// INFO: Business milestones visible in production
log.info("Payment initiated for orderId={} amountCents={}", orderId, amountCents);
if (amountCents > 1_000_000) {
// WARN: Recoverable abnormal condition
log.warn("High-value transaction detected for orderId={}: requires manual review", orderId);
}
}
}Synthesis & Production Verification
Production Acceptance Criteria
- Emit one message per level and explain the audience of each
- Silence com.example debug output via configuration only
- Show parameterized logging costs nothing when disabled
Key Takeaway
SLF4J provides an asynchronous, leveled logging facade that prevents thread contention, enables dynamic runtime filtering, and avoids unnecessary string allocation.
Architectural & Technical Review
Core Interview Questions
Q01Why is System.out.println forbidden in production enterprise Java applications?Show answer
System.out is synchronous and synchronizes on PrintStream, causing severe thread contention under load. It lacks timestamps, thread context, and log levels, and cannot be dynamically filtered or routed.
Q02What are the five standard SLF4J log levels and when should each be used?Show answer
TRACE for high-volume wire detail; DEBUG for troubleshooting internal logic; INFO for major business milestones; WARN for unexpected but recoverable events; ERROR for failures requiring immediate intervention.
Q03Why should logback-spring.xml be preferred over logback.xml in Spring Boot?Show answer
logback-spring.xml allows Spring Boot to initialize logging after environment resolution, enabling <springProfile> and <springProperty> tags to adapt logging per active profile.
Production Pitfalls
Critical Anti-Patterns
- Pitfall: Using System.out.println for application debugging — Blocks worker threads on I/O monitors, degrades performance, and cannot be filtered or redirected.
- Pitfall: Evaluating heavy string concatenations inside disabled debug log statements — String concatenation executes regardless of log level; use SLF4J parameterized placeholders ({}) instead.
Primary Documentation & Specifications
Lesson Primary References
Supplementary Module Resources