Sunday, September 4, 2011

Commons-Logging Tutorial

1. Introduction

The Apache Commons Logging (JCL) provides a Log interface that is intended to be both light-weight and independent of numerous logging toolkits. It provides the middleware/tooling developer a simple logging abstraction, that allows the user (application developer) to plug in a specific logging implementation.

The Apache Commons Logging provides a Log interface with thin-wrapper implementations for other logging tools, including Log4J, Avalon LogKit, and JDK 1.4. The interface maps closely to Log4J and LogKit.

2. Configuration:

There are two base abstractions used by Commons-Logging: Log (the basic logger) and LogFactory (which knows how to create Log instances). UsingLogFactory implementations other than the default is a subject for advanced users only, so let's concentrate on configuring the default implementation.
The default LogFactory implementation uses the following discovery process to determine what type of Log implementation it should use (the process terminates when the first positive match - in order - is found):

  1. Look for a configuration attribute of this factory named org.apache.commons.logging.Log (for backwards compatibility to pre-1.0 versions of this API, an attribute org.apache.commons.logging.log is also consulted).
  2. Look for a system property named org.apache.commons.logging.Log (for backwards compatibility to pre-1.0 versions of this API, a system propertyorg.apache.commons.logging.log is also consulted).
  3. If the Log4J logging system is available in the application class path, use the corresponding wrapper class (Log4JLogger).
  4. If the application is executing on a JDK 1.4 system, use the corresponding wrapper class (Jdk14Logger).
  5. Fall back to the default simple logging wrapper (SimpleLog).

3. Developing:

To make use of Commons-Logging,include the following import statements:
import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory;
Note that some components using commons-logging may either extend Log, or provide a component-specific LogFactory implementation. Review the component documentation for guidelines on how commons-logging should be used in such components.
For each class definition, declare and initialize a log attribute as follows:

public class CLASS {    
private static Log log = LogFactory.getLog(CLASS.class);    ...  

Messages are logged to a logger, such as log by invoking a method corresponding to priority. The org.apache.commons.logging.Log interface defines the following methods for use in writing log/trace messages to the log:
log.fatal(Object message);
log.fatal(Object message, Throwable t);
log.error(Object message);
log.error(Object message, Throwable t);
log.warn(Object message);
log.warn(Object message, Throwable t);
log.info(Object message);
log.info(Object message, Throwable t);
log.debug(Object message);
log.debug(Object message, Throwable t);
log.trace(Object message);
log.trace(Object message, Throwable t);

Semantics for these methods are such that it is expected that the severity, from highest to lowest, of messages is ordered as above.
In addition to the logging methods, the following are provided for code guards:
log.isFatalEnabled(); 
log.isErrorEnabled(); 
log.isWarnEnabled(); 
log.isInfoEnabled(); 
log.isDebugEnabled(); 
log.isTraceEnabled();

General - Message Priorities/Levels

It is important to ensure that log message are appropriate in content and severity. The following guidelines are suggested:
  • fatal - Severe errors that cause premature termination. Expect these to be immediately visible on a status console. See also Internationalization.
  • error - Other runtime errors or unexpected conditions. Expect these to be immediately visible on a status console. See also Internationalization.
  • warn - Use of deprecated APIs, poor use of API, 'almost' errors, other runtime situations that are undesirable or unexpected, but not necessarily "wrong". Expect these to be immediately visible on a status console. See also Internationalization.
  • info - Interesting runtime events (startup/shutdown). Expect these to be immediately visible on a console, so be conservative and keep to a minimum. See also Internationalization.
  • debug - detailed information on flow of through the system. Expect these to be written to logs only.
  • trace - more detailed information. Expect these to be written to logs only.

4. Integration:

The minimum requirement to integrate with another logger is to provide an implementation of the org.apache.commons.logging.Log interface. In addition, an implementation of the org.apache.commons.logging.LogFactory interface can be provided to meet specific requirements for connecting to, or instantiating, a logger.

  • org.apache.commons.logging.Log

  • The default LogFactory provided by JCL can be configured to instantiate a specific implementation of the org.apache.commons.logging.Log interface by setting the property of the same name (org.apache.commons.logging.Log). This property can be specified as a system property, or in the commons-logging.properties file, which must exist in the CLASSPATH.
  • Default logger if not plugged

  • The Apache Commons Logging SPI uses the implementation of the org.apache.commons.logging.Log interface specified by the system propertyorg.apache.commons.logging.Log. If the property is not specified or the class is not available then the JCL provides access to a default logging toolkit by searching the CLASSPATH for the following toolkits, in order of preference:
    • Log4J
    • JDK 1.4
    • JCL SimpleLog
  • org.apache.commons.logging.LogFactory

If desired, the default implementation of the org.apache.commons.logging.LogFactory interface can be overridden, allowing the JDK 1.3 Service Provider discovery process to locate and create a LogFactory specific to the needs of the application. Review the Javadoc for the LogFactoryImpl.java for details.

5. Configuring logger implementation:

As Log4J is the default logger, a few details are presented herein to get the developer/integrator going.Configure Log4J using system properties and/or a properties file:

  • log4j.configuration=log4j.properties
  • Use this system property to specify the name of a Log4J configuration file. If not specified, the default configuration file is log4j.properties.  
  • log4j.rootCategory=priority [, appender]*
  • Set the default (root) logger priority.  
  • log4j.logger.logger.name=priority
  • Set the priority for the named logger and all loggers hierarchically lower than, or below, the named logger. logger.name corresponds to the parameter of LogFactory.getLog(logger.name), used to create the logger instance. Priorities are: DEBUG, INFO, WARN, ERROR, or FATAL.Log4J understands hierarchical names, enabling control by package or high-level qualifiers: log4j.logger.org.apache.component=DEBUG will enable debug messages for all classes in both org.apache.component and org.apache.component.sub. Likewise, setting log4j.logger.org.apache.component=DEBUG will enable debug message for all 'component' classes, but not for other Jakarta projects.
  • log4j.appender.appender.Threshold=priority
Log4J appenders correspond to different output devices: console, files, sockets, and others. If appender's threshold is less than or equal to the message priority then the message is written by that appender. This allows different levels of detail to be appear at different log destinations.For example: one can capture DEBUG (and higher) level information in a logfile, while limiting console output to INFO (and higher).

6. Examples:

As it was explained in my previous blog post Log4j Tutorial, we'll use the same sample examples here to commons-logging work.

First download the, commons-logging-1.1.1-bin.zip
by clicking on the above link.

Keep the commons-logging jar in the classpath. I'll be using eclipse builder to try out the examples.

Example 1:

My example code for demonstrating commons logging is Try1.java. It is as shown.


Maintain the directory structure as shown, provided commons-logging and log4j jars are in the classpath.



The contents in the "commons-logging.properties" must be as follows.



The contents in the "log4j.properties" must be as follows.



The output will be as shown.


Example 2:

Now using "log4j.xml"

The content of code is almost the same. But we must specify the log4j configuration file as log4j.xml instead of log4j.properties. The log4j.xml is as shown.


The output will be the same as previous example.

NOTE: By default, the commons-logging jars(if they are in the classpath) look for the default logging being used. Suppose log4j jars are in classpath, then by default it'll set the log4j.configuration as log4j.properties or log4j.xml ( In other words, it;ll look for those two files). So if we have both log4j and commons-logging jars in classpath it is not mandatory to use the "commons-logging.properties" file. This scenario is taken up in the next example.

Example 3:

  1. Create a Project name "SampleLog" in eclipse.
  2. Create two packages. One is named as "com.zinnia.admin" & the other one is named as "com.zinnia.report".
  3. Create two java classes "SampleAdmin.java" & "SampleReport.java", each in one of the packages. The directory structure and the java class placement is as shown.
  4. We can see that log4j and commons-logging libraries are in the classpath along with log4j.xml. Also here there is no commons-logging.properties file. The code for SampleAdmin.java  & SampleReport.java is given below.



The log4j.xml is as follows.




Now when we run the "SampleAdmin.java" class, a directory named "logs" gets created. There will be 2 files inside this directory namely admin.log & report.log .




The contents in "admin.log" and "report.log" is as follows.


It can be concluded that commons-logging is just a wrapper over all different types of loggers like log4j, simplelog or default java logs etc.

For more information on Log4j and different functionalities used in this post like console appenders and file appenders can be referred in Log4j Tutorial .

4 comments:

  1. Thank you. This works in eclipse. But the same doesn't work in Rad with websphere 6.1. Any idea as to how to make it work?? logger.isDebugEnabled() always return false.

    ReplyDelete