WrapLog

Download | API | Forum | Feedback | Project | CVS

WrapLog is a tiny Java logging package that can "wrap" around other logging packages, thus removing the need to stick to a certain one. This is particular useful for Java libraries that want to leave the choice for a logging packing to the client code. Additionally, simple loggers to write to the console or a file are included, so WrapLog can also act as a standalone-logging package for simple projects that do not want to depend on high JDK versions or include several 100KB of logging code.

Features

Download

The current version (and older ones) can be downloaded from the SourceForge download page.

How to use WrapLog

First, download the WrapLog-full.jar and include it on your classpath. The API documentation is included in the JAR, but also is available online

Next, define a class Logger that extends AbstractLogger and implements the missing methods. You might also extend one of the available example implementations, for example SystemLogger, which logs to System.out and System.err.

Library authors should consider to put Logger in net.sf.wraplog and add static getLogger methods to it. That way, authors of client applications can replace it by providing their own net.sf.wraplog.Logger earlier in the classpath. Ideally, these rules should be enforced by proper class design. Unfortunately Java has this a dreadful feature called static methods, which most existing logging packages use. Static methods can not be declared in abstract classes or interfaces, so you have to copy and paste from the examples below.[1]

Here's an example Logger that fulfills all this:

package net.sf.wraplog;

public class Logger extends SystemLogger {
private static Logger logger;

private static synchronized Logger getLogger() {
if (logger == null) {
logger = new Logger();
}
return logger;
}

public static Logger getLogger(String name) {
return getLogger();
}

public static Logger getLogger(Class clazz) {
if (clazz == null) {
throw new NullPointerException("parameter clazz must not be null");
}
return getLogger(clazz.getName());
}
}

Now you can obtain a logger for a certain class and log a message to it:

Logger logger = Logger.getLogger(LoggerTest.class);

You can log different kind of messages:

logger.debug("internal debugging message");
logger.info("doing something");

You can also pass an Exception to a Logger, which is particular useful to log error details:

File imageFile = new File("image.jpg");
try {
FileInputStream stream = new FileInputStream(imageFile);
// Do something with the file
} catch (IOException error) {
logger.error("Cannot read image file \"" + imageFile.getAbsolutePath() + "\"", error);
}

Hierarchical Logging

For many small applications, the simple Logger presented above might be all you need. However, for big libraries and applications it is not very useful to have an application wide logging level. Therefor real logging packages support hierarchical logging, allowing to recursively change the logging level of whole package hierarchies. Provided that the underlying logging package supports this feature, you can also access it from the wrapped logger. For example:

Logger.getLogger("com.my.app.gui").setLevel(Logger.WARN)
Logger.getLogger("com.my.app.gui.ImageViewer").setLevel(Logger.DEBUG);

This sets the logging level to WARN for all classes in the package com.my.app except for ImageViewer, which uses DEBUG. The Log4jLogger supports hierarchical logging. Again, we need to extend it and add getLogger:

package net.sf.wraplog;

import java.util.HashMap;
import java.util.Map;

public class LoggerLog4j extends Log4jLogger {
private static Map loggerMap = new HashMap();

public static LoggerLog4j getLogger(String name) {
LoggerLog4j result;
synchronized (loggerMap) {
result = (LoggerLog4j) loggerMap.get(name);
if (result == null) {
result = new LoggerLog4j(name);
loggerMap.put(name, result);
}
}
return result;
}


public static LoggerLog4j getLogger(Class clazz) {
if (clazz == null) {
throw new NullPointerException("parameter clazz must not be null");
}
return getLogger(clazz.getName());
}

public LoggerLog4j(String name) {
super(name);
}
}

Notice that the only things that really changed is the base class and getLogger(name).

But now that we are using Log4j as the wrapped implementation, do not forget to initialized Log4j properly. The easiest way is to simply call

BasicConfigurator.configure();

before logging something for the first time.

Handling logging errors

In case WrapLog cannot log a message, by default it throws a LoggingException. In case you prefer some other behavior, you can write implement a LoggingExceptionHandler and activate it using AbstractLogger.setLoggingErrorHandler.

Here's an example handler that logs the error and the failed message to System.err:

public class PrintingLoggingErrorHandler implements LoggingErrorHandler {
    public void handleLoggingError(String errorMessage, Throwable error,
            String originalMessage, Throwable originalError) {
        System.err.println("cannot log message: " + errorMessage);
        if (error != null) {
            error.printStackTrace();
        }
        if (originalMessage != null) {
            System.err.println("message to be logged: " + originalMessage);
        }
        if (originalError != null) {
            System.err.println("error to be logged:");
            originalError.printStackTrace();
        }
    }
}

When not to use WrapLog

WrapLog mostly is an ugly solution to an ugly problem. There are several mature logging packages that might already fit your needs.

SourceForge.net Logo


[1] Apart from that, static methods cause a lot of head ache when it comes to error handling, initialization, clean up, and multi threading. If you want to make sure to shoot yourself into the foot, use lots of static methods, preferrably accessing static variables. To avoid all this, use pseudo-singletons with a public static synchronized instance() method.