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.
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);
}
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.
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(); } } }
[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.