Jakarta Commons Logging (with Log4j and JDK Logging)


Many developers still use println statements in their Java programs for tracing and debugging purposes. However, logging frameworks can provide a nice alternative to this because they provide performance benefits and enable us to selectively turn on and off the messages generated by the logging; in addition, they provide various formatting options and the capability to send the messages to multiple devices without requiring us to add extra code in your application.

If you have been working in Java for some time now, you have likely heard of Apache's Log4j (logging.apache.org) and the logging built in to Java Platform Standard Edition Development Kit (JDK version 1.4 or later; java.sun.com). What you might not be aware of is Jakarta Commons Logging (JCL) API.

Instead of rephrasing the description provided on the commons-logging website (jakarta.apache.org/commons/logging/), I'll quote parts of it here because it is well written:

"The Logging package is an ultra-thin bridge between different logging implementations...using commons-logging does allow the application to change to a different logging implementation without recompiling code. Note that commons-logging does not attempt to initialise or terminate the underlying logging implementation that is used at runtime; that is the responsibility of the application. However many popular logging implementations do automatically initialise themselves; in this case an application may be able to avoid containing any code that is specific to the logging implementation used."

As you might guess from this description, JCL supports log4j and JDK logging, and we will use it here because it enables us to avoid coding to a concrete logging implementation. Also, JCL automatically detects which logging implementation is used underneath the coversthat is, log4j or JDK.

JCL can be downloaded from the http://jakarta.apache.org/commons/logging/. The only file we technically need out of this package is the commons-logging.jar file, which needs to be placed in your classpath. Beyond this, we either need JDK 1.4 (or later) or log4j-related files and configuration. I will demonstrate JDK 1.4-based logging here because it is built in to the JDK. For details on the log4j setup, refer to the http://logging.apache.org website.

How JCL Works

JCL is a simple API. The two key components are org.apache.commons.logging. LogFactory (a concrete factory class) and the instance it returns of an object that implements the org.apache.commons.logging. Log interface. What's really nice about JCL is that it requires a bare minimum of configuration. In most cases, if we have the commons-logging.jar (provided with JCL distribution) in the classpath, JCL will configure itself!

Furthermore, the default LogFactory provides a nice automatic discovery process of the underlying logging API. It first attempts to locate log4j.jar in the classpath; if this doesn't exist, it tries to drop back to JDK 1.4 (or later) logging. However, if we are using an old Java Virtual Machine (JVM), commons-logging will fall back on a default and rudimentary built-in logging wrapper.

Developing with JCL

After we have an instance of the org.apache.commons.logging. Log interface, we can call one of the following methods in it:

  • fatal(Object message)

  • error(Object message)

  • warn(Object message)

  • info(Object message)

  • debug(Object message)

  • trace(Object message)

These methods also have counterparts, which take a second parameter of type java.lang. Throwable; for example, log.fatal(Object message, Throwable t).

Let me demonstrate a simple example using JCL. The following code shows the simplest version of the program. I was able to get this programming running by having commons-logging.jar in my classpath:

import org.apache.commons.logging. Log; import org.apache.commons.logging. LogFactory; public class CommonsLoggingTest {     private static Log log = LogFactory.getLog(CommonsLoggingTest.class);     public static void main(String[] args)     {       log.fatal("This is a FATAL message.");       log.error("This is an ERROR message.");       log.warn("This is a WARN message.");       log.info("This is an INFO message.");       log.debug("This is a DEBUG message.");     } }


Message Logging Levels

One important thing to note about logging is the setting of a level for the types of messages we want printed. For example, as we see from the previous CommonsLoggingTest example, there are various methods for the level of logging we want (fatal, for example). The destination, formatting, and threshold of messages generated by your program can then be tweaked via log4j's log4j.properties file or JDK's logging.properties file (see the respective sites for details).

It is important to use the various log methods appropriately. For example, if we want to output only a debug message, we can use the .debug method versus .info or another method. This way, when the output level threshold is in property files set to WARN (in lo4j, for example), our debug messages won't be printed on the console. Similarly, use the fatal for serious errors because we wouldn't want our debug messages printing out when the application has been configured to be quiet versus noisy (discussed next).

An application's logging level is a design issue. In other words, you should decide whether you want your application to be noisy versus quiet; that is, will your application generate only true errors or spew out enormous amounts of trace messages (something I tend to discourage because I prefer quiet logging). Incidentally, the following are logging levels for log4j and JDK:

  • log4j DEBUG, INFO, WARN, ERROR, and FATAL (set in log4j.properties).

  • JDK logging FINEST, FINER, FINE, CONFIG, INFO, WARNING, and SEVERE (set in logging.properties under the JRE lib/ directory or by passing the -Djava.util.logging.config.file parameter to the JVM).

The following lines show the output of CommonsLoggingTest class:

Feb 14, 2006 4:19:55 PM CommonsLoggingTest main SEVERE: This is a FATAL message. Feb 14, 2006 4:19:55 PM CommonsLoggingTest main SEVERE: This is an ERROR message. Feb 14, 2006 4:19:55 PM CommonsLoggingTest main WARNING: This is a WARN message.


The reason you do not see the messages generated by the debug and info methods is that I tweaked the level in my C:\Program Files\Java\jre1.5.0_06\lib\logging.properties file by adjusting this line found in this file: java.util.logging. ConsoleHandler.level = WARNING.

I decided to do a quick test with log4J using Eclipse's Scrapbook feature discussed in Chapter 8, "The Eclipse Phenomenon!" (incidentally, this is a very useful feature).

First, I copied log4j-1.2.11.jar and log4.properties to my classpath. This is all we need to do to switch from JDK logging to log4j, using commons-logging. Then I evaluated the following single-line expression in a scrapbook page:

org.apache.commons.logging. LogFactory.getLog(CommonsLoggingTest.class)    .fatal("This is a FATAL message.");


Notice that I'm still using the commons-logging API, not log4j or JDK logging calls, thus enabling me to decouple my code from the underlying logging framework. The output of the scrapbook page looked like this:

2006-02-17 10:25:50,654 FATAL [CommonsLoggingTest] - This is a FATAL message.


My log4j.properties (located in my classpath) looked like this (notice I'm using only the stdout appender, and not logfile):

log4j.rootLogger=WARN, stdout # log4j.rootLogger=WARN, stdout, logfile log4j.appender.stdout=org.apache.log4j. ConsoleAppender log4j.appender.stdout.layout=org.apache.log4j. PatternLayout log4j.appender.stdout.layout. ConversionPattern=%d %p [%c] - %m%n log4j.appender.logfile=org.apache.log4j. FileAppender log4j.appender.logfile. File=timex.log log4j.appender.logfile.layout=org.apache.log4j. PatternLayout log4j.appender.logfile.layout. ConversionPattern=%d %p [%c] - %m%n


Sample Logging in TimesheetListController

Let's try some sample logging in Time Expression. For example, let's slightly alter the code in the TimesheetListControllerTest.handleRequest method, as shown here:

List timesheets = timesheetManager.getTimesheets(employeeId); Log log = LogFactory.getLog(TimesheetListController.class); log.info("Returning " + timesheets.size() + " rows."); return new ModelAndView(VIEW_NAME, MAP_KEY, timesheets);


Now let's also tweak the log4j.properties file we looked at previously, as follows:

log4j.rootLogger=INFO, stdout


The output of the log.info call shows something like the following:

2006-02-17 10:52:15,648 INFO [com.visualpatterns.timex.controller. TimesheetListC   ontroller] - Returning 4 rows.


A Note About Formatters

Both log4j and JDK logging provide formatters to output your message in a variety of formats, from simple text output to XML format logging (java.util.logging. XMLFormatter, for example) to custom pattern-based layout (org.apache.log4j. PatternLayout, for example).

We can also write custom formatters. This is a feature worth investigating further because you can customize the output to the way you need it and configure the new formatter in the respective configuration files without changing your code.

Using Logging for Spring and Hibernate

If you run into problems with Hibernate or Spring, you can turn the logging level up (that is, DEBUG for log4j and FINEST for JDK logging). This will probably produce more messages than you want to see; however, it can be very helpful, particularly with Hibernate, which shows you the database-specific SQL it generates (behind the scenes).



Agile Java Development with Spring, Hibernate and Eclipse
Agile Java Development with Spring, Hibernate and Eclipse
ISBN: 0672328968
EAN: 2147483647
Year: 2006
Pages: 219

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net