Exceptions and per-request context (a tiny MDC)
The first error kept the exception object, so the handler can print where it came from; the second kept only the message text. Real MDC implementations work the same way, with a ThreadLocal map per thread.
Logging records what a running program did, so you can understand it in production where there's no debugger. Professional Java code logs through the SLF4J API with a back-end such as Logback, uses levels (TRACE to ERROR) to control volume, writes parameterized messages (log.info("Order {} placed", id)) instead of string concatenation, and passes exceptions as the last argument so their stack traces are kept.
Change the code and press Run (Ctrl+Enter). Try to predict the output first, then break it on purpose and read the error. Your edits are saved and match the lesson page.
Practice questions
Write the code in the editor, run it, then open the model answer to compare.
Write a JUL Formatter subclass that formats records as LEVEL|logger|message and use it with your own handler to log three messages at different levels.
Write a method mask(String card) that keeps only the last four digits ("** ** 4242") and log a payment with the masked card number.
Write a JUL handler that counts records per level in a TreeMap and prints the counts after logging five messages.
Explain it without notes
Why use a logging framework instead of System.out.println?
Why should you write log.debug("x {}", x) instead of log.debug("x " + x)?
How should an exception be logged, and what goes wrong otherwise?
What is SLF4J, and why do libraries depend on it instead of on Logback?
What is the MDC, and what is the common bug when using it with thread pools?
Expected output
INFO [orderId=A-1042] checkout started
SEVERE [orderId=A-1042] payment failed
java.lang.IllegalArgumentException: amount must be positive: -5
at Main.charge
INFO [orderId=-] next request, no order yet
SEVERE [orderId=-] payment failed: amount must be positive: 0