Login | Register
My pages Projects Community openCollabNet

GEF and commons-logging


By using commons-logging, the logging package can be changed easily and does not force users of the GEF package to use Log4J, which has been hard-coded into the GEF class files. Many people are now using the java.util.logging package that was introduced with Java 1.4. Converting the GEF code to use commons-logging is pretty much just a search and replace. Now the end user can specify a specific logging package of their choice.

What follows is a fairly detailed description of using each logger supported by commons-logging. This will be of use to developers who are integrating GEF into their application. But first, I'll start off with an overview of logging aimed at GEF developers themselves.

Using commons-logging in GEF

The modifications to the GEF source code to convert from log4j to commons-logging are minimal. For each file that used log4j logging, these changes were made:

Replaced:
import org.apache.log4j.*;

With:
import org.apache.commons.logging.*;
import org.apache.commons.logging.impl.*;

Replaced lines like:
private static Logger LOG = Logger.getLogger(DefaultGraphModel.class);

With:
private static Log LOG = LogFactory.getLog(DefaultGraphModel.class);

Several modifications were made to the build file, specifically in the init and run targets. The init target introduces several new properties to allow the developer to change out the logging system easily, the run target was modified to use those new properties. The default values for the properties set the logging system to use log4j exactly as it has been used in previous releases of GEF.

To use a different logger requires modification of the ${user.home}/.ant.properties file. As this file defines properties for use by the individual developers and is not included with the GEF distribution, it is possible for each developer to use the logging system of their choice. The following example .ant.properties file shows settings to use the Jdk14Logger:

logger=org.apache.commons.logging.impl.Jdk14Logger
logger.configuration.name=java.util.logging.config.file
logger.configuration.value=logger.properties

The logger property specifies the commons-logging logger to use and tells commons-logging to use the Jdk14Logger.
The logger.configuration.name property is the name of a property to pass to java to specify the logger configuration settings, and logger.configuration.value is the value of the logger configuration settings. In this example, the java command is passed

-Djava.util.logging.config.file=logger.properties

This tells java to use the settings in a file named logger.properties to set up the java.util.logging framework. See below for an example of settings found in the logger.properties file. I have added a basic logging.properties file for using the Jdk14Logger that does output similarly to that produced by the default Log4J settings.

By way of comparison, these settings could be placed in your .ant.properties file to specify that Log4J be used:

logger=org.apache.commons.logging.impl.Log4JLogger
logger.configuration.name=log4j.configuration
logger.configuration.value=log4j.properties

These are in fact the default settings in the build file.


Integration with other applications

In the following, I describe changing the logging system from the command line or from an Ant build file. As most of the settings are simple property settings, they can also be done from within your code.

To specify a specific logger be used, set this system property:

org.apache.commons.logging.Log

to one of:
org.apache.commons.logging.impl.Log4JLogger
org.apache.commons.logging.impl.Jdk14Logger
org.apache.commons.logging.impl.SimpleLog
org.apache.commons.logging.impl.LogKitLogger


Where Log4JLogger is the Log4J logger, Jdk14Logger uses the Java 1.4+ java.util.logging classes, SimpleLog sends all messages to System.err, and LogKitLogger uses the Avalon LogKit logging system.

For example:

java -Dorg.apache.commons.logging.Log=org.apache.commons.logging.impl.Log4JLogger MyClass

will cause MyClass to use the Log4J logger.

By default, the commons-logging framework will attempt to use loggers in this order:
1. Log4J
2. Jdk 1.4 logger
3. Simple log

So if the org.apache.commons.logging.Log property is not set and Log4J is found in the classpath, it will be used. Otherwise, if the JVM is version 1.4 or later, the java.util.logging classes will be used. Failing that, the built-in SimpleLog will be used.


To set up Log4JLogger:
Make sure log4j.jar is in the classpath for the application.
Set this system property:

log4j.configuration

to the name of a property file containing the configuration settings for log4j,
for example:

log4j.configuration=log4j.properties

See the log4j home pages for documentation on the specific configuration settings.

For example:

java -Dorg.apache.commons.logging.Log=org.apache.commons.logging.impl.Log4JLogger \
-Dlog4j.configuration=log4j.properties MyClass


will cause MyClass to use the Log4J logger with the configuration settings
found in log4j.properties.

Here's an example of setting this up in an Ant build file:
      <java classname="LogTest" fork="true">
<classpath refid="classpathref"/>
<jvmarg value="-Dorg.apache.commons.logging.Log=org.apache.commons.logging.impl.Log4JLogger"/>
<jvmarg value="-Dlog4j.configuration=log4j.properties"/>
</java>


To set up Jdk14Logger:
Set this system property:

org.apache.commons.logging.Log=org.apache.commons.logging.impl.Jdk14Logger

For example:

java -Dorg.apache.commons.logging.Log=org.apache.commons.logging.impl.Jdk14Logger MyClass

will cause MyClass to use the Jdk14 logger.

The Jdk14 logger can be configured as follows:

Set a property named "handler" to a white-space separated list of
java.util.logging Handler classes, for example:

handler=java.util.logging.ConsoleHandler java.util.logging.FileHandler

The log level can be set per logger or for all loggers at once, for example:

.level=INFO

will cause all loggers to log at INFO level. Better yet, these properties can be
put into a file, then used like this:

java -Dorg.apache.commons.logging.Log=org.apache.commons.logging.impl.Jdk14Logger \
-Djava.util.logging.config.file=logging.properties MyClass


Here's an example property file:

--------------------------------------------------------------------------------
# logging.properties
# jdk handlershandlers=java.util.logging.ConsoleHandler java.util.logging.FileHandler
# default log level
.level=INFO
# log level for the "DEBUG" logger
DEBUG.level=FINE
# log file name for the FileHandler
java.util.logging.FileHandler.pattern=java%u.log
--------------------------------------------------------------------------------

Here's an example of setting this up in an Ant build file:
      <java classname="LogTest" fork="true">
<classpath refid="classpathref"/>
<jvmarg value="-Dorg.apache.commons.logging.Log=org.apache.commons.logging.impl.Jdk14Logger"/>
<jvmarg value="-Djava.util.logging.config.file=logging.properties"/>
</java>

See the code example below for how to set up a named logger, such as the DEBUG
logger mentioned in the above example. More typically, a logger is created per
class, so within MyClass there may be a line like this:

Log log = LogFactory.getLog(MyClass.class);

which is equivalent to

Log log = LogFactory.getLog(getClass().getName());

Then settings in the configuration file can be set per class if needed, for
example:

my.package.MyClass.level=FINEST

The commons-logging log levels correspond to the java.util.logging.Level levels
as follows:

fatal = Level.SEVERE
error = Level.SEVERE
warn = Level.WARNING
info = Level.INFO
debug = Level.FINE
trace = Level.FINEST


In the above example configuration file, notice that the DEBUG logger has a log
level of FINE. This means that "trace" messages will not be recorded. Also, the
default level for the ConsoleHandler is INFO, so "debug" messages will not be
visible in the console output. By changing the .level setting to FINE and adding
java.util.logging.ConsoleHandler.level=FINE, debug messages will then appear in the
console output.

More details on settings for the ConsoleHandler and the FileHandler can be found
in the JDK API documentation.

There is also a MemoryHandler, a SocketHandler, and a StreamHandler available,
see the JDK API documentation for details and specific properties.

To set up SimpleLog:
Set this system property:

org.apache.commons.logging.Log=org.apache.commons.logging.impl.SimpleLog

For example:

java -Dorg.apache.commons.logging.Log=org.apache.commons.logging.impl.SimpleLog MyClass

will cause MyClass to use the Simple logger.

Simple implementation of Log that sends all enabled log messages,
for all defined loggers, to System.err.  The following system properties
are supported to configure the behavior of this logger:

org.apache.commons.logging.simplelog.defaultlog -
    Default logging detail level for all instances of SimpleLog.
    Must be one of ("trace", "debug", "info", "warn", "error", or "fatal").
    If not specified, defaults to "info".
org.apache.commons.logging.simplelog.log.xxxxx -
    Logging detail level for a SimpleLog instance named "xxxxx".
    Must be one of ("trace", "debug", "info", "warn", "error", or "fatal").
    If not specified, the default logging detail level is used.
org.apache.commons.logging.simplelog.showlogname -
    Set to true if you want the Log instance name to be
    included in output messages. Defaults to false.
org.apache.commons.logging.simplelog.showShortLogname -
    Set to true if you want the last componet of the name to be
    included in output messages. Defaults to true.
org.apache.commons.logging.simplelog.showdatetime -
    Set to true if you want the current date and time
    to be included in output messages. Default is false.


In addition to looking for system properties with the names specified
above, the current implementation also checks for a class loader resource named
"simplelog.properties", and includes any matching definitions
from this resource (if it exists).

import org.apache.commons.logging.*;
import org.apache.commons.logging.impl.*;
public class LogTest{
public static void main (String[] args) {

// set up the loggers
Log log = LogFactory.getLog(LogTest.class);
Log debug = LogFactory.getLog("DEBUG");

// tell what they are
System.out.println("log is a " + log.getClass().getName());
System.out.println("debug is a " + debug.getClass().getName());


log.info("an info message");
debug.debug("a debug message");

log.info("another info message");
debug.debug("another debug message");

}
}