JUL configuration

Once you have set SLF4J up, and done a bit of practice using the simple logger native plugin, you would probably start using it coupled to a "real" log framework, like the standard JDK logging facility, friendly known as JUL, from its fully qualified package name: java.util.logging.

To use JUL as logging framework in an application we have just to change the application classpath, removing the previous SLF4J plugin jar and adding slf4j-jdk14-1.6.4.jar instead. This is enough to SLF4J to get that JUL is our requested logging target.

By default JUL will use its default configuration. Usually we'd like to specify our specific configuration, let's see an example.

I wrote a very simple configuration file, named logging.properties, containing this few lines:
#test log
.level= ALL
handlers= java.util.logging.ConsoleHandler
java.util.logging.ConsoleHandler.level= ALL
An hash character, '#', has the same meaning of of a double slash in Java: consider whatever is after, till the end of line, as a comment.
In the second line we see how to specify the generic log level for the application: all the messages have to be generated.
The third line is about which handler have to be used. The console handler is a simple one that sends the log messages to standard error. By default it prints only messages with severity info and above.
In the fourth line we configure the console handler to manage all the messages, overriding its standard behavior.

It is not overkilling, we have to specify both that all the messages have to be generated (line 2) and managed by our handler (line 4).

A simple way to use a JUL configuration file is passing it to Java from the command line:
> java -classpath .;C:/slf4j-1.6.4/slf4j-api-1.6.4.jar;C:/slf4j-1.6.4/slf4j-jdk14-1.6.4.jar 
-Djava.util.logging.config.file=logging.properties test.MyLogTester
We asked java to run a class, test.MyLogTester, loading a file, logging.properties, as java.util.logging.config.file, so that it will be used to set the options for the JDK logger.

We can do the same programmatically. It could be handy to work in this way, for instance, to use different profiles without being forced to stop and restart the application:
Properties prop = System.getProperties();
prop.setProperty("java.util.logging.config.file", "logging.properties"); // 1
try { LogManager.getLogManager().readConfiguration(); } catch(Exception e) {} // 2
1. It doesn't make much sense to use an hard-coded file name, but this is just an example.
2. This error handling is very weak. We should have checked for IOException and SecurityException, and give some feedback to the user in case of failure.

A simple little method to check how the log behavior changes for different loggers and configurations:
void slf() {
if (!log.isTraceEnabled())
    System.out.println("Trace not enabled");
else
    log.trace("trace");

if (!log.isDebugEnabled())
    System.out.println("Debug not enabled");
else
    log.debug("debug");

if (!log.isInfoEnabled())
    System.out.println("Info not enabled");
else
    log.info("info");

if (!log.isWarnEnabled())
    System.out.println("Trace not enabled");
else
    log.warn("warn");

if (!log.isErrorEnabled())
    System.out.println("Error not enabled");
else
    log.error("error");
}
It is interesting to check if a log level is enabled before calling the relevant log message, so that we can have a different output when testing different configuration files.

No comments:

Post a Comment