Lesson 33 / 38

SLF4J & Structured Logging

Stop debugging with System.out.println — log through SLF4J against a real backend like Logback, pick the right level, and understand why that matters in production.

Why not System.out.println

println can't be turned off per class, can't be redirected to a file or log aggregator, carries no timestamp/level/thread info, and is effectively a synchronized call that can serialize your threads. A logging framework gives you all of that for free.

SLF4J: log against an interface

SLF4J is a facade — your code depends only on its Logger interface, and any backend (Logback, Log4j2) can be plugged in via the classpath, without touching a single log statement.

private static final Logger log = LoggerFactory.getLogger(OrderService.class);

void place(Order o) {
    log.debug("placing order {}", o.id());
    try {
        repo.save(o);
        log.info("order {} placed for {}", o.id(), o.customerId());
    } catch (Exception e) {
        log.error("failed to place order {}", o.id(), e);
    }
}

Pick the right level

TRACE/DEBUG for developer detail (off in prod), INFO for significant business events, WARN for recoverable problems worth a look, ERROR for failures needing attention. Use {} placeholders, not string concatenation — SLF4J skips formatting entirely when the level is disabled.