Stage 7: Tools and testing, lesson 2 of 7

Logging with SLF4J and Logback

Intermediate3 min readall versions
Explain it forThe essentials plus production detail and pitfalls.

Use a logging framework, not System.out.println. Spring Boot sets up SLF4J (the API you call) with Logback (the engine) by default.

  • Create one logger per class: private static final Logger log = LoggerFactory.getLogger(X.class);
  • Pick the level: error (needs attention), warn (unexpected but handled), info (business events), debug (details for developers), trace (very detailed).
  • Use placeholders: log.info("Order {} paid", id). The message is only built when that level is enabled.
  • Pass an exception as the last argument to keep the stack trace: log.error("Payment failed for {}", id, e).
  • Set levels per package in application.yml.

In production, log structured JSON (supported since Spring Boot 3.4) with a trace id, and send logs to one central place such as Grafana Loki, the ELK stack or your cloud's log service.

Example

Java
@Service
public class PaymentService {
    private static final Logger log = LoggerFactory.getLogger(PaymentService.class);
    private final Gateway gateway;

    public PaymentService(Gateway gateway) { this.gateway = gateway; }

    public void capture(String orderId, long paise) {
        log.info("Capturing payment for order {} amount {}", orderId, paise);
        try {
            gateway.capture(orderId, paise);
            log.debug("Gateway response received for {}", orderId);
        } catch (GatewayException e) {
            log.error("Payment capture failed for order {}", orderId, e);   // keeps the stack trace
            throw e;
        }
    }
}
application.yml
logging:
  level:
    root: info
    com.javaatlas: debug
    org.hibernate.SQL: debug        # show SQL in development
  structured:
    format:
      console: ecs                  # JSON logs (Spring Boot 3.4+)

Common mistake

Building messages by concatenation, as in log.debug("x=" + expensive()). The string is built even when debug is off; use placeholders.

Under the hood

Never log passwords, tokens, full card numbers or personal data you don't need; logs are copied widely and kept for a long time. Add request context with MDC (MDC.put("orderId", id)) so every line of a request carries the id; Micrometer Tracing adds trace and span ids automatically. Logging too much at info costs money and hides the signal.

Check yourself

How should you log an exception so the stack trace is kept?

How this connects

Know these first

Where this leads

You've reached the end of this thread. Try a learning path for what's next.

Part of Job-ready backend developer, Microservices and production.

Was this lesson helpful?

Finished reading? Mark it complete to track your progress.