Saturday, December 19, 2009

Compiere and Logging

Introduction

Recently I received the mission to provide a tool to unify logging messages from different software into one central place.
With Log4j you can define specific appender for that. One possibility is using Jdbc appender from the different application to a central database. Another way is to use a SNMP appender. Or even write my own appender.
The problem for softwares using log4j is solved : I add the appenders that I want to the configuration file of Log4j (log4j.properties or log4j.xml). Finding this file when running in application servers (JBoss, Tomcat,...) can be a hard piece of work in itself but I will not talk about that here. Google can help.

Compiere used Java Logging

Then comes Compiere that do not use Log4j. Compiere uses Java Logging framework. Java logging is like Log4j but without the possibility to do configuration through configuration file. This was clearly not an option for me to do logging changes by programming, compilation, packaging, deployment changes.

So I decided to change Compiere Logging framework to add the possibility to do Log4j logging.

Compiere Logging implementation

The Java Logging framework is initialized through the java class CLogMgt.java
There are 2 different initialization methods : one if running the GUI Client Compiere, one if running Compiere in "server" mode. The server mode is used in the accounting processor or in the web ui, but also when you use Compiere API in some specific development running in an application server (Web Service, portal, ...)

Handlers or Appenders

CLogConsole

This is an appender that sends messages to System.out and System.err
This the basic Console appender
This appender is only used in Client mode

CLogFile

This is a File appender that looks for environment variable COMPIERE_HOME, then create a directory called log in it, then create files of the form

Compiere2/log/YYYY-MM-DD_X.log where X will begin at 0 and increment
In client Mode the file will have the form

Compiere2/log/clientYYYY-MM-DD_X.log
This file will only be used if the user, using its Tools / Preferences option, activate the Trace file.

CLogErrorBuffer

This a Database appender to the table AD_Issue that only go there for SEVERE and WARNING level

Compiere Formatter

CLogFormatter formats the log output in a static way : date space logger name space message.

Compiere Filter

CLogFilter filters the logger name and avoid logging of classes in packages java.*, javax.*, sun.*

Compiere Logger

CLogger is the logger class that is being used by Compiere classes. It redirects its output to the initialized Handler/Appender defined here.

Adding a Log4J Handler/Appender

The solution I found was to add a 4th appender that will uses Log4j and its possibility to configure by file, not by programming.
My Formatter and Filter can be defined by configuration file.

Changes to CLogMgt.java - initialize()


Handler audaxisHandler = new Log4JHandler();
Filter audaxisFilter = new Log4JFilter();
audaxisHandler.setFilter(audaxisFilter);
addHandler(audaxisHandler);


The Log4JHandler class :


public class Log4JHandler extends java.util.logging.Handler {

@Override
public void publish(LogRecord paramLogRecord) {
Logger log4jLogger = Logger.getLogger(paramLogRecord.getLoggerName());
boolean loggable = log4jLogger.isEnabledFor(Log4JUtil.getPriority(paramLogRecord.getLevel()));
if (loggable) {
log4jLogger.log(Log4JUtil.getPriority(paramLogRecord.getLevel()),
paramLogRecord.getMessage(),
paramLogRecord.getThrown());
}
}

/**
* Compiere does not decide of the Level, Log4J Configuration does.
*/
@Override
public void setLevel(Level level) {
;
}
}


Finally we have to set the Level on the Logger at the maximum Level, ALL because it is up to the different Handlers to decide if they do the log or not do the log.
If we leave the default level on the CLogger, INFO, we will not be able to DEBUG only on Log4J.

Change in CLogger.java:


public static synchronized CLogger getCLogger (String className)
{
...
newLogger.setLevel(Level.ALL); // here the change
manager.addLogger(newLogger);
return newLogger;
}




Conclusion

Using a small change to CLogMgt I am know able to configure my logging mechanism from Compiere.

No comments: