Java Logging Revisited

| | Comments (0) | TrackBacks (0)

I know this goes against the collective wisdom of Java developers everywhere, but I'll say it anyway: Java Logging is not so bad. Granted, its API isn't as simple as Apache Commons Logging or Log4J, and you may end up writing your own Formatter, but its core implementation is sound. Of course, if you depend on libraries such as Spring, they require you to include 'commons-logging.jar' in your classpath. In that case you can still choose to use the Java Logging API directly in your application and tell Commons to do the same by using a 'commons-logging.properties' file. There are numerous other logging utilities that you should also explore when deciding which library to commit to.

So let's assume that you have chosen to use Java Logging, but you are not totally sold on the API. You could choose to use Commons Logging if you don't mind the extra dependency. But with a minimal amount of effort, it's not so difficult to write a thin wrapper around Java Logging that makes it more convenient to use than even the Apache offerings:

public final class Log {
  private Log() { }

  private static final Map<String, Logger> loggers = new HashMap<String, Logger>();

  public static void debug(String message, Object... params) {
    log(Level.FINE, message, params);
  }

  public static void info(String message, Object... params) {
    log(Level.INFO, message, params);
  }

  public static void warn(String message, Object... params) {
    log(Level.WARNING, message, params);
  }

  public static void error(String message, Object... params) {
    log(Level.SEVERE, message, params);
  }

  public static void log(Level level, String message, Object... params) {
    final int stackPositionOfCaller = 2;
    StackTraceElement caller = new Throwable().getStackTrace()[stackPositionOfCaller];
    String className = caller.getClassName();
    Logger logger;
    synchronized (loggers) {
      logger = loggers.get(className);
      if (logger == null) {
        logger = Logger.getLogger(className);
        loggers.put(className, logger);
      }
    }
    if (logger.isLoggable(level)) {
      String formattedMessage;
      Throwable thrown = null;
      if (params.length == 0) {
        formattedMessage = message;
      }
      else {
        Object last = params[params.length - 1];
        if (last instanceof Throwable) {
          Object[] subParams = new Object[params.length - 1];
          System.arraycopy(params, 0, subParams, 0, subParams.length);
          formattedMessage = String.format(message, subParams);
          thrown = (Throwable) last;
        }
        else {
          formattedMessage = String.format(message, params);
        }
      }
      LogRecord record = new LogRecord(level, formattedMessage);
      record.setLoggerName(logger.getName());
      record.setSourceClassName(className);
      record.setSourceMethodName(caller.getMethodName());
      record.setThrown(thrown);
      record.setParameters(params);
      logger.log(record);
    }
  }
}

This particular implementation provides the following advantages:

  • The Logger name is automatically set to be the fully-qualified class name of the caller, by using stack introspection.
  • Log messages are formatted using the String.format() patterns and variable arguments. The parameters are also assigned to the underlying LogRecord.
  • If the last parameter to a method is a Throwable, it will be assigned to the LogRecord.
  • The method names align more closely with the Log4j and Commons Logging method names.

All methods are static wrappers around a Java Logger. Each Logger is created as needed and then cached, one per calling class. Execution time is about the same when compared to direct calls to Logger.

You can download the code and use it as a starting point for your own logging utility. A JUnit test class is included which, in addition to testing the basic API, measures and compares execution times of the Java Logger and the Log class presented here.

Leave a comment


Type the characters you see in the picture above.

0 TrackBacks

Listed below are links to blogs that reference this entry: Java Logging Revisited.

TrackBack URL for this entry: http://www.nearinfinity.com/mt/mt-tb.cgi/465