Logging in Weblogic console with Log4J

If you have developed a JEE web application using Log4J for logging and have it deployed on a WebLogic application server, you may wonder how to display the logs in WebLogic console:

Preparation

You simply need to create and add a Log4J appender. This appender will redirect Log4J events to WebLogic by using the NonCatalogLogger class. You can found this class in wls-api.jar or wlclient.jar (depending on your WebLogic version) from your WebLogic's lib directory. For instance if you are using maven, you need to add one the following dependencies in your maven's pom.xml (enter the version corresponding to your WebLogic installation):

<dependency>
    <groupId>weblogic</groupId>
    <artifactId>wlclient</artifactId>
    <version>10.3</version>
    <scope>provided</scope>
</dependency>

or

<dependency>
    <groupId>weblogic</groupId>
    <artifactId>wls-api</artifactId>
    <version>10.0</version>
    <scope>provided</scope>
</dependency>

Obviously WebLogic JARs are not in official Maven repositories (due to license/distribution restrictions, proprietary softwares always here to hassle you). So type the following command in your shell to add the API in your local maven repository:

$ mvn install:install-file -DgroupId=weblogic -DartifactId=wlclient -Dversion=10.3 -Dpackaging=jar -Dfile=wlclient.jar

or

$ mvn install:install-file -DgroupId=weblogic -DartifactId=wls-api -Dversion=10.0 -Dpackaging=jar -Dfile=wls-api.jar

Creating the appender

The appender needs to implement Log4J's Appender interface, but it's more convenient to extends AppenderSkeleton. WebLogic's NonCatalogLogger class has some debug, info… methods like Log4J so the appender is just going to map one to the other.

Since you may deploy your application on something else than WebLogic (for instance I usually use Tomcat and/or Jetty for development/testing) you don't want have it crashing your application because WebLogic classes are not here. The appender can check if the class is in the classpath (using Class.forName()) and do nothing if the NonCatalogLogger is not here.

In WebLogic's console, there is a "Subsystem" column, we can set it in the appender to display the application name.

package sample.project;

import org.apache.log4j.AppenderSkeleton;
import org.apache.log4j.Level;
import org.apache.log4j.spi.LoggingEvent;

import weblogic.logging.NonCatalogLogger;

public class WeblogicAppender extends AppenderSkeleton {
    private static final String SUBSYSTEM = "SampleProject";
    private NonCatalogLogger logger;

    public WeblogicAppender() {
        try {
            Class.forName("weblogic.logging.NonCatalogLogger");
            logger = new NonCatalogLogger(SUBSYSTEM);
        } catch (ClassNotFoundException e) {
            // Not running on WebLogic server.
        }
    }

    @Override
    protected void append(LoggingEvent event) {
        if (logger == null) {
            return;
        }
        if (Level.TRACE.equals(event.getLevel())) {
            logger.trace(getMessage(event), getThrowable(event));
        } else if (Level.DEBUG.equals(event.getLevel())) {
            logger.debug(getMessage(event), getThrowable(event));
        } else if (Level.INFO.equals(event.getLevel())) {
            logger.info(getMessage(event), getThrowable(event));
        } else if (Level.WARN.equals(event.getLevel())) {
            logger.warning(getMessage(event), getThrowable(event));
        } else if (Level.ERROR.equals(event.getLevel())) {
            logger.error(getMessage(event), getThrowable(event));
        } else if (Level.FATAL.equals(event.getLevel())) {
            logger.critical(getMessage(event), getThrowable(event));
        }
    }

    @Override
    public void close() {
        // Nothing to do here.
    }

    @Override
    public boolean requiresLayout() {
        return false;
    }

    private String getMessage(LoggingEvent event) {
        return String.valueOf(event.getMessage());
    }

    private Throwable getThrowable(LoggingEvent event) {
        if (event.getThrowableInformation() != null) {
            return event.getThrowableInformation().getThrowable();
        } else {
            return null;
        }
    }
}

Log4J configuration

In your log4j.xml> just define a new appender using the above class and add it to the root logger. Here is an example:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
  <appender name="console" class="org.apache.log4j.ConsoleAppender">
    <param name="Target" value="System.out" />
    <layout class="org.apache.log4j.PatternLayout">
      <param name="ConversionPattern" value="%-5p %c{1} - %m%n" />
    </layout>
  </appender>

  <appender name="weblogic" class="sample.project.WeblogicAppender" />

  <root>
    <priority value="debug" />
    <appender-ref ref="console" />
    <appender-ref ref="weblogic" />
  </root>
</log4j:configuration>

Check

I have created a simple servlet logging some messages in debug, info and warn levels, let's call it and see what happens:

It works! (OK, I was not going to write all that just to show something that does not work 😉). But be careful, as you can see, the debug message is not displayed even after setting Log4J's level to debug. My WebLogic console is configured to display higher level messages. Don't forget to check that it's logging at the right level for you.

Notes

All source code in this post is in public domain, do what ever you want with it. I did it quickly so it may not work as you want, you are strongly advised to adapt it to your needs. I'm not mastering WebLogic at all (in fact I don't really like that application server) so it may not be the best way to do it (and usually you should not need to do such things), and it is not at all certified in any way to be "production ready".

Comments Add one by sending me an email.