In a previous article, I covered a Java logging API called log4j. In that article, I mentioned that if you were going to be using the Java 2 SDK version 1.4, a logging API was included in the standard distribution. At that time, I felt that with the release of the logging API in the standard JDK, it was better to stick with log4j since it was more mature and proven. I still use log4j in my projects, but I decided it was time to take a look at the built-in logging, see how it is implemented, and find out how well it works compared to the log4j package.
If you are not familiar with the log4j package, you may want to read the previous article before continuing. As I write this article, the most recent log4j release is 1.2.4. It features two changes that make it more compatible with the JDK logging API implemented. The changes involve the Category class moving to the Logger class and the Priority class moving to the Level class. The Category and Priority classes are still supported, so your existing log4j code will work if you are upgrading from a previous version. Now, let’s take a closer look at adding logging to your applications.
Put a Logger in your code
The main object your code will use to interface with the logging API is the Logger class. The Logger class contains the necessary methods for retrieving a Logger instance and for logging various levels of messages. You can use code similar to the following to get an instance of the Logger class:
private static final Logger logger = Logger.getLogger(JavaLoggingExample.class.getName());
This code retrieves an instance of the Logger using the current class name. In this case, the name of the class is JavaLoggingExample. The getLogger() method accepts a String argument specifying the Logger class name. The logging API uses a hierarchical namespace so that settings can be inherited to lower levels of the hierarchy (more on settings in a moment). For instance, if the full name of the class were com.versatilesolutions.builder.JavaLoggingExample, the settings for the Logger could be inherited from com.versatilesolutions since it is considered the parent of the class. All Loggers inherit from the LogManager such that newly created Loggers will have the settings declared in the LogManager.
Logs are based upon levels, which are represented by integers. The Level class in the java.util.logging package contains fields for the preset levels. The levels are SEVERE, WARNING, INFO, CONFIG, FINE, FINER, and FINEST. To log a message of a particular level, you call the log method with the level and message you desire:
logger.log(Level.SEVERE, “This is a severe level message!”);
This logs a SEVERE level message. There are a number of log methods designed to save time by logging directly to a particular level:
logger.severe(“This is another severe level message!”);
When you run these log methods, the output to the console looks something like this:
Jun 25, 2002 4:40:34 PM JavaLoggingExample
SEVERE: This is a severe level message!
The first line lists the date and time information followed by the name of the class and the logging API’s best guess as to the routine that made the call. In this case, indicates the constructor of the class. The logging API does not guarantee the accuracy of the trace information since accurate stack traces can be affected by many of the optimized runtime environments.
You use handlers and formatters to customize logging. A handler lets you control the destination for your log statements. The formatter, as you might guess, controls the actual output. The API includes these handlers: ConsoleHandler, SocketHandler, and FileHandler. If you browse the API Javadocs, you will see other handlers listed, but they are just support classes used for other handlers. Only two formatters are included in the standard API—SimpleFormatter and XMLFormatter.
The entire logging configuration is accomplished through a property file that is read from the lib directory within the JRE directory. This file is named logging.properties. You can also adjust the logging properties dynamically by declaring and adding a handler and or formatter to a Logger instance. I’ve created a small test class (
) that establishes a file handler in addition to the default console handler. By default, this file handler uses XMLFormatter. You can see the file (output) in
You may adjust the logging properties by specifying an alternate property file or class using a Java system property (java.util.logging.config.class or java.util.logging.config.file). The configuration class will be instantiated and will be responsible for configuration.
Standard API or log4j?
After examining the standard logging API, my opinion is to stick with the log4j package if you are looking for a robust logging implementation. The logging API is adequate, but it is missing many of the features available in log4j. For instance, log4j already has a file rotation class that can rotate based upon the date. (You could extend the FileHandler class in the logging API, but why create more work for yourself?)
Another log4j advantage is the ease with which you can configure it by loading a configuration object with a specified property file. The logging API’s method of specifying the file or class as a system property seems difficult to manage, and in my projects, I like a simple startup. Log4j also has a much more robust formatting system that allows you to apply simple patterns to your log output without having to write a class that extends the formatter. The numerous add-on programs and handlers available for the log4j package make it an excellent choice, since almost anything you would need is probably already available.
There are ways to make the logging API a robust solution for many logging needs, but at this time, I think the log4j package offers a better solution. Still, if you just want some simple console, socket, or file logging, the logging API is already built into the JDK.
(July 24, 2002 – http://builder.com.com/5102-22-1046694.html)