Lesson 41 • Advanced
Logging with SLF4J & Logback
System.out.println() is for learning. Professional logging is for production. When your app crashes at 2 AM, your logs are the only witness. SLF4J + Logback gives you structured, searchable, level-filtered logs that actually help you debug — fast.
Before You Start
You should know Exception Handling (logging exceptions properly) and Maven & Gradle (adding logging dependencies). Understanding Multithreading helps for MDC context.
What You'll Learn
- ✅ SLF4J as a logging facade
- ✅ Log levels: TRACE, DEBUG, INFO, WARN, ERROR
- ✅ Logback configuration and appenders
- ✅ MDC (Mapped Diagnostic Context) for request tracing
- ✅ Structured logging (JSON format)
- ✅ Log rotation and production best practices
1️⃣ Log Levels — When to Use Each
Key insight: In production, you typically run at INFO level. TRACE and DEBUG messages are silently discarded (zero cost with parameterized logging). When investigating issues, temporarily lower to DEBUG for a specific package without restarting.
| Level | When to Use | Production |
|---|---|---|
| TRACE | Method entry/exit, loop iterations | Off |
| DEBUG | Variable values, algorithm steps | Off |
| INFO | App started, user logged in, order placed | On ✅ |
| WARN | Retry attempt, deprecated API, slow query | On ✅ |
| ERROR | Unhandled exceptions, failed payments | On ✅ + alerts |
Try It: Logging System Simulation
// 💡 Try modifying this code and see what happens!
// Professional logging system simulation
console.log("=== Logging System ===\n");
// Simulated SLF4J Logger
const LEVELS = { TRACE: 0, DEBUG: 1, INFO: 2, WARN: 3, ERROR: 4 };
let currentLevel = LEVELS.INFO; // Production default
class Logger {
constructor(name) { this.name = name; }
log(level, msg, ...args) {
if (LEVELS[level] < currentLevel) return; // Filtered out!
let formatted = msg.replace(/\{\}/g, () => args.shift() ??
...2️⃣ MDC & Structured Logging
Analogy: MDC is like a name badge that every log message from a request automatically wears. When 1000 requests are processing simultaneously, MDC lets you filter logs by requestId to see exactly one request's journey through your system.
MDC.put("requestId", uuid) — attach to thread context
%X{requestId} — include in log pattern
MDC.clear() — clean up in finally block
Try It: MDC & Structured JSON Logging
// 💡 Try modifying this code and see what happens!
// MDC request tracing and structured JSON logging
console.log("=== MDC & Structured Logging ===\n");
// Simulated MDC (thread-local context)
let MDC = {};
class StructuredLogger {
constructor(name) { this.name = name; }
log(level, msg) {
// Plain text format
let plain = new Date().toISOString().substr(11, 12) + " " + level.padEnd(5) + " " +
(MDC.requestId ? "[" + MDC.requestId + "] " : "") +
(MDC.userId ? "user="
...Common Mistakes
- ⚠️ String concatenation:
log.debug("User " + name)wastes CPU. Uselog.debug("User {}", name) - ⚠️ Logging sensitive data: Never log passwords, credit cards, tokens, or PII
- ⚠️ Catching and swallowing:
catch(Exception e) {}is invisible failure. Always log the exception - ⚠️ Using System.out.println: Not filtered, not structured, not searchable. Use SLF4J
Pro Tips
- 💡 MDC — attach requestId/userId to every log. Essential for microservice tracing
- 💡 Structured JSON logging — log as JSON so Elasticsearch/CloudWatch can parse and search
- 💡 Lombok's @Slf4j — one annotation replaces the logger boilerplate
- 💡 Log rotation — configure max file size and history to prevent disk fill
Try It: Log Rotation & Alerting
// 💡 Try modifying this code and see what happens!
// Log rotation and alerting simulation
console.log("=== Log Rotation & Alerting ===\n");
// 1. Log rotation configuration
console.log("1. LOG ROTATION CONFIG:");
console.log(` <appender name="FILE" class="RollingFileAppender">
<file>logs/app.log</file>
<rollingPolicy class="SizeAndTimeBasedRollingPolicy">
<fileNamePattern>logs/app.%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
<maxFileSize>100MB</maxFileSize>
<maxHisto
...📋 Quick Reference
| Concept | API | Purpose |
|---|---|---|
| Get logger | LoggerFactory.getLogger(X.class) | SLF4J factory |
| Parameterized | log.info("msg {}", val) | Lazy string eval |
| MDC | MDC.put("key", "value") | Thread-local context |
| Markers | MarkerFactory.getMarker("AUDIT") | Categorize entries |
| Lombok | @Slf4j | Auto-generate logger |
🎉 Lesson Complete!
You've mastered professional logging — your logs will now be structured, searchable, and actionable! Next: Secure Coding — preventing vulnerabilities in Java applications.
Sign up for free to track which lessons you've completed and get learning reminders.