8.2 Logging

Logging always degrades performance. The penalty you pay depends to some extent on how logging is done. One possibility is using a final static variable to enable logging, as in the following code:

public final static boolean LOGGING = true;
...
if (LOGGING)
  System.out.println(...);

This code allows you to remove the logging code during compilation. If the LOGGING flag is set to false before compilation, the compiler eliminates the debugging code.[2] This approach works well when you need a lot of debugging code during development but don't want to carry the code into your finished application. You can use a similar technique for when you do want logging capabilities during deployment, by compiling with logging features but setting the boolean at runtime.

[2] See Section 6.1.4 and Section 3.9.1.4.

An alternative technique is to use a logging object:

public class LogWriter {
  public static LogWriter TheLogger = sessionLogger(  );
  ...
}
...
LogWriter.TheLogger.log(...)

This technique allows you to specify various LogWriter objects. Examples include a null log writer that has an empty log( ) method, a file log writer that logs to file, a sysout log writer that logs to System.out, etc. Using this technique allows logging to be turned on after an application has started. It can even install a new type of log writer after deployment, which can be useful for some applications. However, be aware that any deployed logging capabilities should not do too much logging (or even decide whether to log too often), or performance will suffer. The logging framework introduced in 1.4, java.util.logging, provides most of the features you should need. There is also an open source implementation of the logging APIs for JDK Versions 1.2 and 1.3 available from http://javalogging.sourceforge.net/. The following is an example of using the 1.4 logging framework.

import java.util.logging.*;
  
  ...
  // Get a Logger object. Use a name to distinguish it.
  Logger globalLogger = Logger.getLogger("global");
  // Log an INFO level message
  globalLogger.info("Starting application.");
  try
  {
    ... //do something
  }
  catch(Exception e)
  {
    //log a level SEVERE message, including the exception
    globalLogger.log(Level.SEVERE, "Oh dear, this is bad.", e);
    //And a level WARNING message
    globalLogger.warning("Exiting");
    return;
  }
  
  //Something went well if we're here, so just send a fairly
  //low level message: level FINE message for debugging
  globalLogger.fine("Bad things didn't happen.");
  ...

The logging API was designed to minimize overhead when using configurable logging. Given that there are multiple levels of logging, where some levels may be turned off, those logging statements that are turned off should produce negligible overhead to the application, and those that are turned on should impose as small an overhead as possible. In addition, the logging API enables the conversion and output of logging statements to be handled separately, so that overhead from these activities can be minimized. The logging API is covered in more detail in Learning Java by Pat Niemeyer and Jonathan Knudsen (O'Reilly), and a basic introduction can be obtained in an OnJava article by Brian Gilstrap.[3]

[3] "An Introduction to the Java Logging API," Brian Gilstrap, OnJava.com, June 2002, http://www.onjava.com/pub/a/onjava/2002/06/19/log.html.

I recommend deploying applications with a simple set of logging features in place. But first ensure that the logging features do not slow down the application.