Getting started
The heart of the logging API is a class called Logger. This is the class that is responsible for outputting log information. The following shows a simple example of a class declaring a Logger and outputting a message:
package logging;
import java.util.logging.Logger;
public class SimpleLogging {
public static void main(String[] args) {
Logger log = Logger.getLogger(SimpleLogging.class.getName());
log.info("This is an information message");
}
}
If I run this program in Eclipse it produces the following output:
Apr 25, 2014 2:39:02 PM logging.SimpleLogging main
INFO: This is an information message
In many ways this is similar to using System.out.println, but you can already see some advantages.
• The output includes the current date and time that the message was logged
• The output includes information about the class that produced the output
You will look at these features in more detail shortly, but for now, turn your attention to the line that creates the logger:
Logger log = Logger.getLogger(SimpleLogging.class.getName());
The getLogger static helper returns an object that can be used for logging messages. Because there will be many Loggers in a complex program it is important to associated a String with each Logger so that it is possible to differentiate the output of one Logger from another.
The most common String to associate with a Logger is the name of the class the Logger is declared in – and this is the reason that the previous example output the class name – in reality it was outputting the Logger name.
Notice that the method I have used in this example is info. When you log messages you assign an importance to them via a level – info indicates that the output is for informational purposes only. The level can also be assigned explicitly:
package logging;
import java.util.logging.Level;
import java.util.logging.Logger;
public class SimpleLogging {
public static void main(String[] args) {
Logger log = Logger.getLogger(SimpleLogging.class.getName());
log.log(Level.FINEST, "This is a finest message");
log.log(Level.FINER, "This is a finer message");
log.log(Level.FINE, "This is a fine message");
log.log(Level.INFO, "This is a info message");
log.log(Level.WARNING, "This is a warning message");
log.log(Level.SEVERE, "This is a severe message");
}
}
This example shows the six most common levels, from the least important to the most important.
If you run this example you may be surprised by the results. I received the following output:
Apr 25, 2014 3:24:01 PM logging.SimpleLogging main
INFO: This is a info message
Apr 25, 2014 3:24:01 PM logging.SimpleLogging main
WARNING: This is a warning message
Apr 25, 2014 3:24:01 PM logging.SimpleLogging main
SEVERE: This is a severe message
The first three messages have not been printed. The reason for this is that each Logger has a default level set against it, and unless the logged message is at least as important as this level, the message will not be printed.
The reason for this becomes obvious when you reflect back on the reason you are logging information. Information is being logged typically to help understand what is happening with the program. In some situations, however, you may only want to know about severe messages (when the program is being run by end users), while other times you may want much more information (when the program is being run in development or by testers).
If you want the Logger to print all messages (or messages above a specified importance), you begin by specifying this on the Logger before logging any information:
log.setLevel(Level.ALL);
You may think this would be sufficient to print the extra messages. The beauty of levels, however, is that you may wish to log fine messages to the console, while you want to log severe messages to a file – therefore each output destination also has a default logging level.
In order to see this, create an executable JAR from this project using the techniques described in the previous chapter (I called mine simplelogging.jar). Place this somewhere on your file system, create a file called logging.properties in the same directory, and add the following to it:
handlers=java.util.logging.ConsoleHandler
java.util.logging.ConsoleHandler.level=FINE
This configuration file states that you want to perform logging to the console, and that you want to log all messages that are FINE or higher.
If you now execute this as follows:
java -Djava.util.logging.config.file=logging.properties -jar simplelogging.jar
(make sure you are in the directory with the JAR file and the logging.properties file).
It should print the following:
Apr 25, 2014 4:30:59 PM logging.SimpleLogging main
FINE: This is a fine message
Apr 25, 2014 4:30:59 PM logging.SimpleLogging main
INFO: This is a info message
Apr 25, 2014 4:30:59 PM logging.SimpleLogging main
WARNING: This is a warning message
Apr 25, 2014 4:30:59 PM logging.SimpleLogging main
SEVERE: This is a severe message
If you play around with the level you will see more or fewer lines logged.
The console is an example of a handler, but you can add others. Change the logging.properties file as follows:
handlers=java.util.logging.FileHandler, java.util.logging.ConsoleHandler
java.util.logging.ConsoleHandler.level=FINE
java.util.logging.FileHandler.level=INFO
java.util.logging.FileHandler.pattern=simple.log
java.util.logging.FileHandler.limit=10000
java.util.logging.FileHandler.formatter=java.util.logging.SimpleFormatter
This now states that the program should log messages to both a file and the console. The file logger will only log messages that are INFO or higher, and these will be logged to a file called simple.log. This can grow in size to a maximum of 10000 bytes, at which time it will be recycled.
The last line associates a formatter with the FileHandler. By default the FileHandler creates XML output, whereas I just want the simple output that is logged to the console.
If you run this now you should see a file called simple.log created with the following contents:
Apr 25, 2014 4:38:44 PM logging.SimpleLogging main
INFO: This is a info message
Apr 25, 2014 4:38:44 PM logging.SimpleLogging main
WARNING: This is a warning message
Apr 25, 2014 4:38:44 PM logging.SimpleLogging main
SEVERE: This is a severe message
Handlers do not need to be limited to consoles and files. There are many other potential destinations for log messages, such as emails, databases and phone alerts. The handler mechanism abstracts the manner in which messages are logged from the process of logging messages. This is a very flexible design, because it means any program with logging code can be extended to support different approaches to logging without changing the program itself.
So far I have focused on logging messages, but another important consideration is logging stack traces. The following program demonstrates the logging of an error. In this program the c method will generate an exception when it is invoked by main because it is being passed a null String:
package logging;
import java.util.logging.Level;
import java.util.logging.Logger;
public class SimpleLogging {
public static void main(String[] args) {
Logger log = Logger.getLogger(SimpleLogging.class.getName());
log.setLevel(Level.ALL);
try {
a(null);
} catch (Exception e) {
log.log(Level.SEVERE, "An error occured in main", e);
}
}
public static void a(String s) {
b(s);
}
public static void b(String s) {
c(s);
}
public static void c(String s) {
s.substring(0, 10);
}
}
When you run this it should produce the following output:
Apr 26, 2014 2:13:57 PM logging.SimpleLogging main
SEVERE: An error occured in main
java.lang.NullPointerException
at logging.SimpleLogging.c(SimpleLogging.java:27)
at logging.SimpleLogging.b(SimpleLogging.java:23)
at logging.SimpleLogging.a(SimpleLogging.java:19)
at logging.SimpleLogging.main(SimpleLogging.java:12)
Notice how simply passing the exception to the log method causes the entire stack trace to be printed, thereby allowing you to see the entire call stack of the program when the exception occurred.