Logging
Logging is the process of reporting information about a running program. In a debugged program, this information can be ordinary status data that describes the progress of the program (for example, if you have an installation program, you may log the steps taken during installation, the directories where you stored files, startup values for the program, etc.).
Logging is also very useful during debugging. Without logging, you might try to decipher the behavior of a program by inserting println( ) statements. Many examples in this book use that very technique, and in the absence of a debugger (a topic that will be introduced shortly), it’s about all you have. However, once you decide the program is working properly, you’ll probably take the println( ) statements out. Then if you run into more bugs, you may need to put them back in. It’s much nicer if you can put in some kind of output statements, which will only be used when necessary.
Prior to the availability of the logging API in JDK 1.4, programmers would often use a technique that relies on the fact that the Java compiler will optimize away code that will never be called. If debug is a static final boolean and you say:
if(debug) {
System.out.println("Debug info");
}
then when debug is false, the compiler will completely remove the code within the braces (thus the code doesn’t cause any run-time overhead at all when it isn’t used). Using this technique, you can place trace code throughout your program and easily turn it on and off. One drawback to the technique, however, is that you must recompile your code in order to turn your trace statements on and off, whereas it’s generally more convenient to be able to turn on the trace without recompiling the program by using a configuration file that you can change to modify the logging properties.
The logging API in JDK 1.4 provides a more sophisticated facility to report information about your program with almost the same efficiency of the technique in the preceding example. For very simple informational logging, you can do something like this:
//: c15:InfoLogging.java
import com.bruceeckel.simpletest.*;
import java.util.logging.*;
import java.io.*;
public class InfoLogging {
private static Test monitor = new Test();
private static Logger logger =
Logger.getLogger("InfoLogging");
public static void main(String[] args) {
logger.info("Logging an INFO-level message");
monitor.expect(new String[] {
"%% .* InfoLogging main",
"INFO: Logging an INFO-level message"
});
}
} ///:~
The output during one run is:
Jul 7, 2002 6:59:46 PM InfoLogging main
INFO: Logging an INFO-level message
Notice that the logging system has detected the class name and method name from which the log message originated. It’s not guaranteed that these names will be correct, so you shouldn’t rely on their accuracy. If you want to ensure that the proper class name and method are printed, you can use a more complex method to log the message, like this:
//: c15:InfoLogging2.java
// Guaranteeing proper class and method names
import com.bruceeckel.simpletest.*;
import java.util.logging.*;
import java.io.*;
public class InfoLogging2 {
private static Test monitor = new Test();
private static Logger logger =
Logger.getLogger("InfoLogging2");
public static void main(String[] args) {
logger.logp(Level.INFO, "InfoLogging2", "main",
"Logging an INFO-level message");
monitor.expect(new String[] {
"%% .* InfoLogging2 main",
"INFO: Logging an INFO-level message"
});
}
} ///:~
The logp( ) method takes arguments of the logging level (you’ll learn about this next), the class name and method name, and the logging string. You can see that it’s much simpler to just rely on the automatic approach if the class and method names reported during logging are not critical.