Encodera

Thanks for reading!

Donate to the site — we work hard for you.

Contents

Logging

A log is a record of what a program did: it started, accepted an order, failed to connect to the database. The log is how you find out what went wrong on a server where you can't run a debugger. You could write such records with System.out.println, but they have no time and no importance, you can't turn them off without editing the code, and they get mixed with the program's normal output. That's what loggers are for.

System.Logger

The logger built into the JDK is obtained with System.getLogger(name). The name usually matches a package or class and helps you see where a record came from. The log method takes an importance level and a message:

void main() {
    System.Logger log = System.getLogger("shop");
    log.log(System.Logger.Level.INFO, "Order accepted");
}

Output (your date and time will be different):

Oct 04, 2026 10:30:29 AM Main main
INFO: Order accepted

The logger added the time and the class and method the record came from. It writes to System.err, not System.out, so records can be redirected separately from the program's output.

Record format

By default System.Logger hands records over to the JDK's standard logger, java.util.logging. Its format is set by the java.util.logging.SimpleFormatter.format system property: in it %1$s is the date, %3$s the logger name, %4$s the level, %5$s the message, %6$s the exception, and %n a line break. The property is passed with the -D option or set at the start of the program:

void main() {
    System.setProperty("java.util.logging.SimpleFormatter.format", "[%4$s] %3$s: %5$s%6$s%n");
    System.Logger log = System.getLogger("shop");
    log.log(System.Logger.Level.INFO, "Order accepted");
}

Output:

[INFO] shop: Order accepted

Levels

System.Logger.Level has five main levels — from the most detailed to the most important: TRACE, DEBUG, INFO, WARNING, ERROR. By default records from INFO up are shown, and DEBUG and TRACE — details for debugging — are hidden. In the output the levels have their own names: ERROR becomes SEVERE, DEBUG becomes FINE:

void main() {
    System.setProperty("java.util.logging.SimpleFormatter.format", "[%4$s] %5$s%6$s%n");
    System.Logger log = System.getLogger("shop");
 
    log.log(System.Logger.Level.DEBUG, "Cart: 3 items");
    log.log(System.Logger.Level.INFO, "Order accepted");
    log.log(System.Logger.Level.WARNING, "Stock is running low");
    log.log(System.Logger.Level.ERROR, "Payment declined");
}

Output:

[INFO] Order accepted
[WARNING] Stock is running low
[SEVERE] Payment declined

Parameters and exceptions

Values are inserted into a message with {0}, {1} — the string is built only if the record is actually printed. An exception is passed as the last argument, and the logger prints it together with the call stack:

void main() {
    System.setProperty("java.util.logging.SimpleFormatter.format", "[%4$s] %5$s%6$s%n");
    System.Logger log = System.getLogger("shop");
 
    log.log(System.Logger.Level.INFO, "Order {0} from {1}", 42, "Anna");
    try {
        Integer.parseInt("abc");
    } catch (NumberFormatException e) {
        log.log(System.Logger.Level.ERROR, "Could not read the quantity", e);
    }
}

Output (stack lines from the JDK are omitted):

[INFO] Order 42 from Anna
[SEVERE] Could not read the quantity
java.lang.NumberFormatException: For input string: "abc"
	at Main.main(Main.java:7)

java.util.logging directly

The same mechanism can be used directly. The java.logging module isn't part of java.base, so you need an import. The methods are named after the levels: severe, warning, info, config, fine, finer, finest:

import java.util.logging.*;
 
void main() {
    System.setProperty("java.util.logging.SimpleFormatter.format", "[%4$s] %3$s: %5$s%6$s%n");
    Logger log = Logger.getLogger("shop.orders");
 
    log.info("Order accepted");
    log.fine("Details for debugging");
    log.warning("Payment is delayed");
}

Output:

[INFO] shop.orders: Order accepted
[WARNING] shop.orders: Payment is delayed

Real projects most often use the SLF4J library with the Logback implementation (Spring Boot includes them by default): a logger is created with LoggerFactory.getLogger(...), and parameters are inserted with {}. The ideas are the same — name, levels, parameters — and the settings live in a logback.xml file.

Common mistake

To see debug records, you set the logger's level to FINE — but they still don't appear. The reason is that there are two filters: on the logger and on the handler that prints records to the console. By default the handler lets through only INFO and above, so its level has to be lowered too:

import java.util.logging.*;
 
void main() {
    System.setProperty("java.util.logging.SimpleFormatter.format", "[%4$s] %5$s%6$s%n");
    Logger log = Logger.getLogger("shop.orders");
    log.setLevel(Level.FINE);
 
    log.fine("not visible: the handler drops FINE");
 
    Logger.getLogger("").getHandlers()[0].setLevel(Level.FINE);
    log.fine("visible now");
}

Output:

[FINE] visible now

Logger.getLogger("") is the root logger, which owns the console handler. In larger programs such settings go into a logging.properties file, passed with the -Djava.util.logging.config.file=logging.properties option.

Thanks for reading!

Donate to the site — we work hard for you.