Replace System.out with structured logging — wire up SLF4J and Logback, use log levels, and add context with placeholders.
Why: println gives you no levels, no timestamps, and no way to switch detail on or off. A logging library does all that. SLF4J is the standard facade (the API you code against) and Logback is a common implementation (the engine that writes the output).
Add both to Maven (pom.xml):
Why: each class gets its own logger, named after the class, so output shows where it came from. Create it once as a private static final field — the standard pattern you will see everywhere.
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class OrderService {
private static final Logger log = LoggerFactory.getLogger(OrderService.class);
void process() {
log.info("processing started");
}
}Why: levels rank messages by importance — trace, debug, info, warn, error. You can configure which levels actually appear, so you keep detailed debug logs in development and only warnings and errors in production.
log.debug("detailed info for developers"); // usually hidden in prod
log.info("normal events worth recording");
log.warn("something unexpected but recoverable");
log.error("a failure that needs attention");Why: use {} placeholders instead of building the message with +. SLF4J only fills them in if the level is actually enabled, so you avoid wasted string work for messages that get dropped.
String user = "Ada";
int orderId = 42;
log.info("user {} placed order {}", user, orderId);
// -> "user Ada placed order 42"
// log an exception by passing it as the last argument:
try {
throw new RuntimeException("payment failed");
} catch (Exception e) {
log.error("could not process order {}", orderId, e); // logs the stack trace too
}