Thursday, 30 July 2009

Multiple Log4J instances in a WebApp

Many times I've come across the classic problem of different logging instances in an web app server. Most of them involve classloader conflicts:
  • The webapp has log4j.jar but the webapp has bundled log4j.jar as well and you get errors.
  • OR you have the log4j.jar in the application server's shared library directory and you have 2 webapps but the configuration conflict - or even just a configuration for the application server itself and one for the webapp.

I've finally come across a solution for log4j. It involves using a repository selector based on a context, this is fully described here - Supporting the log4j RepositorySelector in Servlet Containers.

So the solution to stop conflicts and independently configure each application is this.
First put ALL logging jars in the shared library directory including (log4j.jar, slf4j-api.jar, slf4j-log4j.jar, commons-logging.jar, etc). This ensures that if they one of the libraries is loaded in the main classloader it can access the other jars.
Next is a little extension for log4j: log4j-contrib. Put its jar into the shared library as well.

Next you have to configure each individual app. Add the following to the web.xml (at the top so it is the first called).
<!-- Use log4j.properties from the classpath -->
<context-param>
<param-name>log4jConfigLocations</param-name>
<param-value>logj.properties</param-value>
</context-param>
<listener>
<listener-class>com.mathieucarbou.log4j.web.ContextLoggingListener</listener-class>
</listener>


If you want to have Tomcat use log4j (not just the apps), then add the following configuration:

For catalina.bat add somewhere in the file (line 119):
rem JavaUtilLogging to SLF4J required to be on classpath.
set CLASSPATH=%CLASSPATH%;%CATALINA_HOME%\lib\jul-to-slf4j-1.5.6.jar;%CATALINA_HOME%\lib\slf4j-api-1.5.6.jar;%CATALINA_HOME%\lib\slf4j-log4j12-1.5.6.jar;%CATALINA_HOME%\lib\log4j-1.2.15.jar

Or under linux, catalina.sh add somewhere appropriate (line 163):
# JavaUtilLogging to SLF4J required to be on classpath.
CLASSPATH="$CLASSPATH":"$CATALINA_HOME"/lib/jul-to-slf4j-1.5.6.jar:"$CATALINA_HOME"/lib/slf4j-api-1.5.6.jar:"$CATALINA_HOME"/lib/slf4j-log4j12-1.5.6.jar:"$CATALINA_HOME"/lib/log4j-1.2.15.jar

Additionally if you put the log4j.properties in somewhere other than CATALINA_HOME/lib then you'll need to add that directory to the classpath. e.g.
rem if you put it in /conf
set CLASSPATH=%CLASSPATH%;%CATALINA_HOME%\conf

Now alter conf/logging.properties and replace the entire contents with:
handlers = org.slf4j.bridge.SLF4JBridgeHandler
.handlers = org.slf4j.bridge.SLF4JBridgeHandler

And that's it for the command line.

Running it from Eclipse

Eclipse has a guide how to set it up at Enable JULI logging in Eclipse. To set it up, with the configuration files stored in the workspace under Servers for a server named "Tomcat6-Localhost" add the following to the end of the Server's launch configuration arguments:
-Dlog4j.configuration="log4j.properties"
-Djava.util.logging.config.file="${workspace_loc:Servers/Tomcat6-Localhost-config/logging.properties}"
-Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager
Copy in logging.properties and log4.properties into the workspace configuration directory as per the command line version.

Note: the argument -Dlog4j.configuration, sets the file name to search for on the Tomcat start up classpath. You CANNOT specify the exact file to load, instead you have to add the folder as a class folder (add external folder in eclipse), in order for it to be located.

Under the launch configuration classpath, add the following jars/folders:
  • jdk:lib/tools.jar
  • bin/bootstrap.ja
  • lib/jul-to-slf4j-1.5.6.jar
  • lib/slf4j-api-1.5.6.jar
  • lib/slf4j-log4j12-1.5.6.jar
  • lib/log4j-1.2.15.jar
  • workspace:Servers/Tomcat6-Localhost-config

No comments:

Post a Comment