Tuesday, January 3, 2012

Apache Log4j to the Eclipse Log

The Problem:
You have written a ton of Java code in your Eclipse Plugins using Apache Log4j and you want to automagically show logged elements in the Error Log view.

The Solution:

1. Create an Appender for your Logger.

The append(LoggingEvent event) method should do as follows:

@Override
protected void append(LoggingEvent event)
String loggerName = event.getLoggerName();// this should be the classname of the class the logger was created in
Plugin plugin = Activator.getDefault(); // default plugin, this one
Bundle bun = plugin.getBundle(); // default bundle, this one
ILog log = plugin.getLog(); // default log, this one.
Class theClass = null;

try {
theClass = Class.forName(loggerName);
bun = FrameworkUtil.getBundle(theClass);
log = Platform.getLog(bun);
} catch (ClassNotFoundException e) {
// did not find it, use defaults
}

// look up the throwable if any
Throwable throwable = null;
if (event.getThrowableInformation() != null){
throwable = event.getThrowableInformation().getThrowable();
}

// construct the Status to log
Status status = new Status(toStatusSeverity(event.getLevel()),
bun.getSymbolicName(),
Status.OK,
event.getRenderedMessage(),
throwable);
if (log != null){
log.log(status);
}
}


2. You need to convert the Event's Level to an IStatus.Level, something like this:

public static int toStatusSeverity(Level eventLevel){
switch (eventLevel.toInt()){
case Level.WARN_INT:
return Status.WARNING;
case Level.ERROR_INT:
case Level.FATAL_INT:
return Status.ERROR;
case Level.TRACE_INT:
case Level.INFO_INT:
case Level.DEBUG_INT:
return Status.INFO;
}
return Status.INFO;
}


3. Special Sauce
In the MANIFEST.MF file for the plugin that has this Appender class in it, add the following line:
DynamicImport-Package: *
This allows the classloader to find classes from other plugins, ie Class.forName will work if you have this (assuming the class exists and you don't have some dummy name for your logger).

Notes:
When you construct your Logger, make sure you do it with the classname for the class it is in, and add the appender, ie:

private static final Logger logger = Logger.getLogger(MyLocalClass.class);
{
logger.addAppender(MyErrorLogAppender.INSTANCE);
}