Logger Demystified
The Logging framework is very easy to understand. A global LogManager object (java.util.logging.LogManager class) keeps an inventory of all global logging information, such as the logging control properties read from the configuration file, and maintains a collection of all named Loggers and Handlers. As indicated earlier, a hierarchical namespace using the dot notation ensures the global uniqueness of the Loggers maintained by the LogManager. Fortunately, the static getLogger() method of the Logger class, which most developers use, hides the complexity of using the LogManager, and we rarely use the LogManager directly.
An application calls a Logger object and provides it with the logging details. The Logger object allocates a LogRecord object, which encapsulates information specific to the call passed by the application explicitly, such as the name, log level, message, and the object array, and also some implicit contextual information such as the time, a unique sequence number, the thread ID, and so on. If the client application doesn't specify an explicit source method name and source class name, the LogRecord class infers these by analyzing the call stack.
Consider the Loggers as the publishers of the LogRecord information. This information is consumed or subscribed by the Handler objects. The Logger object loops through two collections of Handlersthe global handler collection that's maintained by the LogManager class, and the local handler collection, managed by the particular Logger instance. Both Loggers and Handlers make use of logging Levels and Filters to determine whether they want to use a particular LogRecord. A LogRecord with a high enough priority is passed by the Logger to the Handler. To publish the LogRecord and suitably format the message before publishing it to an I/O stream, a Handler can make use of a Formatter object. The separation between the Handler and the Formatter allows for the same Formatter to be used by multiple Handlers of information. Each Handler object references a single Formatter object.
Finally, the Filter interface, which is rarely used, provides an alternative way for a Logger or a Handler to screen LogRecords. It's more flexible, as it allows you to custom develop a filter mechanism that can use any of the attributes available at the time the event is called, rather than using the log Levels. However, this is more expensive computationally.
The good news is that the logging API provides a lot of options for the Handler and the Formatter objects. The java.util.logging provides the following Handlers:
ConsoleHandler writes the formatted records to System.err
FileHandler logs the records either to a single file or to a set of rotating log files.
SocketHandler writes the formatted records to a TCP port.
MemoryHandler buffers the records in memory.
StreamHandler writes the records to an OutputStream.
However, if you want to log the LogRecords in, say, a JMS queue, a database, or any other mechanism, you can do so either by writing a Handler from scratch or by subclassing one of the existing Handlers.
Similarly, the logging API provides two Formatters:
SimpleFormatter writes a brief summary of a log record.
XMLFormatter writes a detailed XML node for each log record.
As with the Handler objects, it's easy to develop your own custom Formatters.