Using the JDK1.4 logging package

Let’s start with a simple self-explanatory example.

Main.java:

import java.util.logging.*;
import java.io.*;
 
public class Main
{
   public static void main(String argv[]){
      Logger logger = Logger.getLogger("test");
 
      logger.setLevel(Level.ALL);
 
      logger.severe("Log message #1");
      logger.warning("Log message #2");
      logger.info("Log message #3");
      logger.config("Log message #4");
      logger.fine("Log message #5");
      logger.finer("Log message #6");
      logger.finest("Log message #7");
   }
}

outputs:

Jan 20, 2002 12:55:02 AM Main main
SEVERE: Log message #1
Jan 20, 2002 12:55:02 AM Main main
WARNING: Log message #2
Jan 20, 2002 12:55:02 AM Main main
INFO: Log message #3

A Level object determines the importance of a log record. The order of levels are important as they control what is being logged. Here’s the predefined hierarchy:

   SEVERE (highest value) 
   WARNING 
   INFO 
   CONFIG 
   FINE 
   FINER 
   FINEST (lowest value) 
 
   ALL    - log all messages
   OFF    - turn off logging

For example, setting the logging level to CONFIG will log messages that are categorized as SEVERE, WARNING, INFO and CONFIG.

So, why then is the above program only logging the first three log messages. After all, we asked the logger object to log all messages with Logger.setLevel(Level.ALL).

To understand why this is not working, you’ll have to know a bit about the whole logging architecture.

Handlers:

A logger is associated with a set of log Handlers. A Handler is a gateway to a particular logging destination resource. For example, there exist a ConsoleHandler that publishes log messages to System.err. There are a number of other handlers included (eg. FileHandler, ConsoleHandler, SocketHandler, …) and you can easily create one yourself.

Formatters:

A handler can have a formatter associated with it. The formatter converts a log record to a String in a particular format, eg. XML.

Levels, Filters

When you ask the logger to log a message, the logger first checks whether it is interested at all. It does so by comparing the level associated with the log message with its own minimum log level. If it’s not interested (the level of the log message is less than its minimum log level), it returns immediately. If it is interested, it will pass the log message to a filter, if one is set. When you create a filter, you have complete control over what message is logged based on the contents of the log message or whatever, just return true (log) or false (don’t log). When the filter approves it (or no filter is defined), the log message is passed to all Handlers that are registered with the Logger.

Likewise, a Handler itself has a minimum log level and can be further customized with a filter.

LogManager

All loggers that are managed by a LogManager. A LogManager is a singleton that keeps track of a hierarchical namespace of Logger objects. It also contains functionality to configure Logger objects. For example, it reads its default configuration from lib/logging.properties.

log parents

Every Logger has a parent. According to the API, it is its nearest existing ancestor in the Logger dot-separated namespace.
The line

   Logger logger = Logger.getLogger("test");

will create a logger object in the namespace test and will register itself with the LogManager. Its parent logger is RootLogger (an inner class in LogManager).

   Logger logger1 = Logger.getLogger("test");
   Logger logger2 = Logger.getLogger("test.a");

In this example, logger1′s parent is RootLogger and logger2′s parent is logger1. When a logger is asked to log a message, by default it will also pass that message to its parents. For example:

Main.java:

import java.util.logging.*;
import java.util.*;
import java.io.*;
 
public class Main
{
   public static void main(String argv[]){
      Logger logger1 = Logger.getLogger("test");
      Logger logger2 = Logger.getLogger("test.a");
 
      logger2.severe("this is a log message");
   }
}

will output:

Jan 20, 2002 2:49:39 AM Main main
SEVERE: this is a log message

The process here is as follows: logger2 is asked to log a message. It passes the minimum log level test (default is INFO and Level.SEVERE > Level.INFO). There are no log handlers associated with logger2 so the message is passed to logger1. Same issue with logger1 that will pass the message to the RootLogger. By default, the RootLogger has one handler registered: ConsoleHandler. [Take a look at jre/lib/logging.properties where the default handlers and levels are set].

Let’s modify the example to add our own ConsoleHandler:

Main.java:

import java.util.logging.*;
import java.util.*;
import java.io.*;
 
public class Main
{
   public static void main(String argv[]){
      Logger logger1 = Logger.getLogger("test");
      Logger logger2 = Logger.getLogger("test.a");
 
      ConsoleHandler ch = new ConsoleHandler();
      ch.setLevel(Level.FINE);
      logger2.addHandler(ch);
 
      logger2.severe("this is a log message");
   }
}

outputs:

Jan 20, 2002 2:58:27 AM Main main
SEVERE: this is a log message
Jan 20, 2002 2:58:27 AM Main main
SEVERE: this is a log message

Here, both logger2 and RootLogger will log the message. You could prevent logger2 from calling its parent by invoking logger2.setUseParentHandlers(false).

Back to the first example and the question why the logger is only logging the first three messages. By now, you should be able to figure out what’s wrong. This diagram may help you:

   +--------------------+
   | RootLogger         |
   | Level: INFO        |
   | Handlers:          |
   |   - ConsoleHandler |
   |     Level: INFO    |
   +--------------------+
            |
   +------------------+
   | Logger "test"    |
   | Level: ALL       |
   | Handlers:        |
   |   (none)         |
   +------------------+

If we want to display all messages, we can do a number of things. We could register our own ConsoleHandler with the logger “test”:

Main.java:

import java.util.logging.*;
import java.io.*;
 
public class Main
{
   public static void main(String argv[]){
      Logger logger = Logger.getLogger("test");
 
      logger.setLevel(Level.ALL);
      ConsoleHandler ch = new ConsoleHandler();
      ch.setLevel(Level.ALL);
      logger.addHandler(ch);
 
      logger.severe("Log message #1");
      logger.warning("Log message #2");
      logger.info("Log message #3");
      logger.config("Log message #4");
      logger.fine("Log message #5");
      logger.finer("Log message #6");
      logger.finest("Log message #7");
   }
}

outputs:

Jan 20, 2002 3:14:29 AM Main main
SEVERE: Log message #1
Jan 20, 2002 3:14:29 AM Main main
SEVERE: Log message #1
Jan 20, 2002 3:14:30 AM Main main
WARNING: Log message #2
Jan 20, 2002 3:14:30 AM Main main
WARNING: Log message #2
Jan 20, 2002 3:14:30 AM Main main
INFO: Log message #3
Jan 20, 2002 3:14:30 AM Main main
INFO: Log message #3
Jan 20, 2002 3:14:30 AM Main main
CONFIG: Log message #4
Jan 20, 2002 3:14:30 AM Main main
FINE: Log message #5
Jan 20, 2002 3:14:30 AM Main main
FINER: Log message #6
Jan 20, 2002 3:14:30 AM Main main
FINEST: Log message #7

Notice now that we get the first three messages outputted twice, once by the logger “test” and once by its parent RootLogger. To turn this off, call logger.setUseParentHandlers(false).

More later when 1.4 is released. The logging API has changed quite a bit during the 1.4 BETA versions.

Meanwhile, look at the other question/answers for more examples that make use of the java.util.logging package.