GAE and Log4j - Getting them to work a bit better together

Sunday, 3 October 2010

The Problem
App Engine supports Log4j, and you'll be able to see your log4j statements come through either as a stdout or stderr message. The trouble that I've had is that if you want to make full use of the nice log highlighting GAE supports in its console then all stdout / stderr messages will come through as a warning.

A good portion of Java frameworks use Log4j as their logging framework. I'm using Apache Wicket as my front-end framework for example, and when user has hit upon a bug on a page, I'd like to know about it as an error and not just a warning.

A Solution
Here's a Log4j Appender which I've written that creates JUL log statements based on original Log4j log events. A Log4j -> JUL bridge if you like, passing on the original severity level to JUL. It's pretty basic, but it works for me!

Here's the code for the Appender itself:
 * @author Eurig Jones
public class GAELogAppender extends AppenderSkeleton
    private static final Logger L = Logger.getLogger("log4j");

    protected void append(LoggingEvent event)
        StringBuilder b = new StringBuilder();
        Level level = event.getLevel();
        String s[] = event.getThrowableStrRep();


        if (s != null)
            for (String line : s)

        if (level.equals(Level.FATAL))
        if (level.equals(Level.ERROR))
        if (level.equals(Level.WARN))
        if (level.equals(Level.INFO))
        if (level.equals(Level.DEBUG))
        if (level.equals(Level.TRACE))

    private void FATAL(String msg)

    private void ERROR(String msg)

    private void WARN(String msg)

    private void INFO(String msg)

    private void DEBUG(String msg)

    private void TRACE(String msg)

    public boolean requiresLayout()
        return false;

    public void close()
Now all we need to do is setup the and configurations correctly to make it work. Here is a snippet of my
#log4j.appender.A1.layout.ConversionPattern=%d ...


log4j.logger=ERROR, A1

..., A1
Above shows that I've replaced the original ConsoleAppender - the default appender which GAE uses, with the new GAELogAppender. This allowing all Log4j statements to go through this instead of the console.

Now the GAE file...
.level = INFO
As you can see, I've added the GAELogAppender here to allow all severities to be filtered through. This means that anything that hits our new appender will reach the GAE logging system, as long as our will allow it to filter through.

I'm still a bit unsure of how exactly GAE Maps JUL severities in it's frontend. In JUL you have FINEST, FINER, INFO, WARNING, SEVERE but the front-end seems to display these differently. It has an ERROR and CRITICAL as well as a WARNING. SEVERE seems to be flagged as an ERROR, but how can you flag something as CRITICAL? Maybe it's in some documentation I've probably scanned through :-)

This is a quick implementation of this Appender for me and so far it works, but I'd love to hear some feedback about what it doesn't do (or doesn't do properly) as I've not used it in anger.