Logging & Observability
SLF4J and Logback, log levels, structured logging, and basic metrics.
On this page
When an app runs in production, you can't attach a debugger - you need it to tell you what it's doing. Logging and observability are how you understand a live system: what happened, when, and why. Skimp on this and production problems become guessing games.
Logging: leave a trail
A log is a timestamped record of events. Good logs are your eyes into a running system.
An aircraft's black box continuously records what's happening so investigators can reconstruct events after the fact. Logs are your application's black box: when something goes wrong at 3 a.m., they're often the only record of what led up to it. Log thoughtfully, and future-you can piece the story together.
Use SLF4J + Logback, not println
System.out.println has no timestamps, levels, or control. Real apps use a
logging framework: SLF4J (the standard API) with Logback (the
implementation).
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
class OrderService {
private static final Logger log = LoggerFactory.getLogger(OrderService.class);
void process(Order order) {
log.info("Processing order {}", order.id()); // {} is a safe placeholder
try {
// ...
} catch (Exception e) {
log.error("Failed to process order {}", order.id(), e); // logs stack trace
}
}
}Use placeholders, not string concatenation
Write log.info("Order {}", id) rather than log.info("Order " + id). The
placeholder version skips building the string entirely if that log level is
disabled - cheaper and cleaner. And always pass the exception as the last argument
so the stack trace is logged.
Log levels
Levels let you control verbosity - chatty in development, quiet in production:
| Level | Use for |
|---|---|
ERROR | Something failed and needs attention |
WARN | Something odd, but the app continues |
INFO | Notable business events (order placed) |
DEBUG | Detailed diagnostic info for developers |
TRACE | Very fine-grained, rarely enabled |
You configure which levels to actually output per environment - no code changes needed.
Never log secrets
Passwords, tokens, credit-card numbers, and personal data must never appear in logs - logs are widely accessible and long-lived. Redact or omit sensitive fields. A leaked log file has caused many real breaches.
Structured logging
Instead of plain text, structured logs (often JSON) attach machine-readable fields, so tools can search and filter them:
{"time":"...","level":"INFO","orderId":42,"msg":"Order placed","userId":7}This makes logs searchable at scale - "show every ERROR for user 7 today" becomes a simple query.
The three pillars of observability
Logging is one of three complementary tools:
- Logs - discrete events ("order 42 failed").
- Metrics - numbers over time (requests/sec, error rate, memory use).
- Traces - the path of a single request across services (where did the time go?).
Metrics and health checks
Spring Boot Actuator exposes metrics and a /health endpoint out of the box.
Tools like Micrometer, Prometheus, and Grafana collect and visualize
metrics so you can watch trends and get alerted before users notice a problem.
Quick check
Which log level should you use for an event that failed and needs attention?
Key takeaways
- Logs are a timestamped record - your window into a running system.
- Use SLF4J + Logback with parameterized messages (log.info("{}", x)), not System.out.println.
- Log levels (ERROR, WARN, INFO, DEBUG, TRACE) control verbosity per environment.
- Never log secrets or personal data; redact sensitive fields.
- Structured (JSON) logs are searchable at scale.
- Observability has three pillars: logs (events), metrics (numbers over time), and traces (request paths).
Finally, let's get your app out the door: packaging and deployment.