16.2 Java Logging API Basics: Logger, Levels, Handlers, and Configuration
Key Takeaways
java.util.logging defines seven standard levels from SEVERE (1000) down to FINEST (300), plus OFF and ALL.
With the default JDK configuration the root logger and its ConsoleHandler use INFO, so fine() messages are not printed.
A log record must pass the logger's level and then each handler's level, so lowering only the logger level to FINE still hides FINE output.
Records also go to parent handlers unless setUseParentHandlers(false) is called, which is why an added handler can print duplicate lines.
The java.util.logging.config.file system property loads a logging.properties file with entries such as .level=INFO and com.shop.level=FINE.
16.2 Java Logging API Basics: Logger, Levels, Handlers, and Configuration
Oracle also expects candidates to "understand the basics of Java Logging API." The standard API is java.util.logging in the java.logging module, so a modular application needs requires java.logging;. The exam-level skills are obtaining a logger, choosing levels, predicting which messages print, and reading a basic configuration.
Obtaining and Using a Logger
import java.util.logging.Level;
import java.util.logging.Logger;
public class OrderService {
private static final Logger LOG = Logger.getLogger(OrderService.class.getName());
void place(int id, int tries) {
LOG.info("Placing order " + id);
LOG.log(Level.WARNING, "Order {0} failed after {1} tries", new Object[] {id, tries});
LOG.fine(() -> "Expensive debug text " + id); // Supplier: built only if FINE is enabled
try {
throw new IllegalStateException("payment declined");
} catch (IllegalStateException e) {
LOG.log(Level.SEVERE, "Order failed", e); // prints the message plus the stack trace
}
}
}
Logger.getLogger(name)returns the logger for a dot-separated name, usually the class name. Calling it again with the same name returns the same instance (log == Logger.getLogger("com.shop.Orders")istruewhile the logger is in use).- Loggers form a hierarchy by name:
com.shop.Ordersis a child ofcom.shop, and the root logger has the empty name"". - Convenience methods exist for each level (
severe,warning,info,config,fine,finer,finest), pluslog(Level, String),log(Level, String, Object[])with{0}-style parameters, andlog(Level, String, Throwable). isLoggable(Level)tells you whether a message at that level would pass the logger's level check.
The Seven Standard Levels
| Level | intValue() | Typical use |
|---|---|---|
SEVERE | 1000 | Serious failures |
WARNING | 900 | Potential problems |
INFO | 800 | Informational messages (the default threshold) |
CONFIG | 700 | Configuration details |
FINE | 500 | Tracing information |
FINER | 400 | Detailed tracing |
FINEST | 300 | Most detailed tracing |
Level.OFF (Integer.MAX_VALUE) disables logging, and Level.ALL (Integer.MIN_VALUE) enables everything. A message is published when its level value is greater than or equal to the threshold.
Why fine() Prints Nothing by Default
With the JDK's default configuration, the root logger's level is INFO and it has one ConsoleHandler, also at level INFO, that writes to System.err. A new named logger has a null level, so it inherits the effective level INFO from its parent. As a result, info, warning, and severe messages appear, while config, fine, finer, and finest messages do not.
Every message must pass two filters:
- The logger's level (its own or the inherited one).
- The handler's level for each handler that receives the record.
Logger log = Logger.getLogger("com.shop.Orders");
log.setLevel(Level.FINE);
log.fine("A"); // still NOT shown: the root ConsoleHandler is INFO
ConsoleHandler handler = new ConsoleHandler();
handler.setLevel(Level.FINE);
log.addHandler(handler);
log.info("B"); // printed TWICE: by this handler and by the root handler
log.setUseParentHandlers(false);
log.fine("C"); // printed once, by this handler only
Records travel up the hierarchy to the parent's handlers unless setUseParentHandlers(false) is called. Forgetting that step is the usual cause of duplicate log lines.
Configuring Logging Without Code
The default settings come from the JDK's logging.properties file. You can point the JVM at your own file with a system property:
java -Djava.util.logging.config.file=logging.properties -cp app.jar com.shop.Main
handlers=java.util.logging.ConsoleHandler, java.util.logging.FileHandler
.level=INFO
com.shop.level=FINE
java.util.logging.ConsoleHandler.level=FINE
java.util.logging.FileHandler.pattern=%h/shop%u.log
.levelsets the root logger level;com.shop.levelsets the level for thecom.shoplogger and its children.- Each handler has its own
levelproperty, which is the second filter described above. FileHandlerwrites to files (its pattern can use%hfor the user's home directory and%ufor a unique number), andConsoleHandlerwrites toSystem.err.
System.Logger: The Platform Logging Facade
Java 9 added System.getLogger(name), which returns a System.Logger with its own levels (ALL, TRACE, DEBUG, INFO, WARNING, ERROR, OFF). By default it is backed by java.util.logging, so System.getLogger("sys").log(System.Logger.Level.INFO, "started") prints through the same console handler. Libraries use it to log without choosing a framework for the application.
How a Log Record Flows
| Step | Component | What can stop the record |
|---|---|---|
| 1 | Logger | The record's level is below the logger's effective level |
| 2 | Logger Filter (optional) | setFilter(...) rejects the record |
| 3 | Each Handler on this logger, then on each parent (unless setUseParentHandlers(false)) | The record's level is below that handler's level, or the handler's own filter rejects it |
| 4 | The handler's Formatter | Nothing; it only turns the record into text (SimpleFormatter by default, XMLFormatter for FileHandler) |
The default SimpleFormatter prints two lines per record: a timestamp with the source class and method, then LEVEL: message. Its layout comes from the java.util.logging.SimpleFormatter.format property; for example, the pattern %4$s: %5$s%n prints only the level and the message, such as INFO: formatted.
Logging Best Practices the Exam Rewards
- Avoid building text that is never logged.
LOG.fine("Order " + id)builds the string even whenFINEis disabled; preferLOG.fine(() -> "Order " + id), a{0}parameter, or anif (LOG.isLoggable(Level.FINE))guard. - Log the exception object, not just its message.
LOG.log(Level.SEVERE, "Order failed", e)keeps the stack trace; logginge.getMessage()loses it. - Do not log and rethrow at every layer. Logging the same exception in each method as it propagates produces duplicate noise; log it once where it is handled.
- Never log secrets. Passwords read with
Console.readPassword()(see 14.1) or tokens must not reach log files. - Use named loggers.
Logger.getLogger(MyClass.class.getName())lets configuration target a package.Logger.getGlobal()returns the logger namedglobal, andLogger.getAnonymousLogger()returns an unregistered logger whose name isnull.
Using the JDK's default logging configuration, which lines appear on the console?
Logger log = Logger.getLogger("com.app.Main");
log.config("one");
log.info("two");
log.fine("three");
log.warning("four");
all four lines
four only
two and four only
one, two, and four
With the default configuration, a developer runs:
Logger log = Logger.getLogger("com.app.Repo");
log.setLevel(Level.FINE);
log.fine("query plan");
Why is nothing printed?
Because setLevel takes effect only after the JVM restarts
Because fine() requires a Supplier argument instead of a String
Because the logger name must match a class that is already loaded
Because the root ConsoleHandler still discards records below INFO
What is printed by the following code under the default logging configuration?
Logger log = Logger.getLogger("com.app.Svc");
ConsoleHandler h = new ConsoleHandler();
log.addHandler(h);
log.info("ready");
The ready message twice, from the new handler and the root handler
The ready message once, because the new handler replaces the root handler
An IllegalStateException, because a logger can have only one handler
Nothing, because a new ConsoleHandler starts at level OFF
Sections you finish are checked off in the contents.
You've completed this section
Continue exploring other exams