Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Custom java.util.logging Handler in tomcat

We have some common logging configuration between all of the webapps on a given system that we are trying to externalize to the level of tomcat, rather than trying to handle at the level of the individual webapp. The webapps that are using java.util.logging are proving to be somewhat challenging because we have a custom handler, and there doesn't seem to be an obvious way to get the custom handler to play nicely with tomcat's classloaders. This is all in the prototype stage at the moment.

Preliminary: Tomcat 7.0.32, Java 6. Default tomcat 7 install with one REST service deployed and nothing funny in the configuration.

First, roughly following the advice in this answer, I created the custom handler and put the jar in $CATALINA_HOME/lib and confirmed that said directory was in the right directory and that the common.loader included this directory:

common.loader=${catalina.base}/lib,${catalina.base}/lib/*.jar,${catalina.home}/lib,${catalina.home}/lib/*.jar

Then I modified the logging.properties file and added the handler:

handlers = 1catalina.org.apache.juli.FileHandler, 2localhost.org.apache.juli.FileHandler, 3manager.org.apache.juli.FileHandler, 4host-manager.org.apache.juli.FileHandler, java.util.logging.ConsoleHandler, my.custom.Handler

When I run ./startup.sh, however, I get the following:

[Loaded java.io.PrintWriter from /System/Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Classes/classes.jar]
[Loaded java.util.logging.StreamHandler from /System/Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Classes/classes.jar]
[Loaded java.util.logging.ConsoleHandler from /System/Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Classes/classes.jar]
Handler error
java.lang.ClassNotFoundException: my.custom.Handler
    at java.net.URLClassLoader$1.run(URLClassLoader.java:202)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.net.URLClassLoader.findClass(URLClassLoader.java:190)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:306)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:301)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:247)
    at org.apache.juli.ClassLoaderLogManager.readConfiguration(ClassLoaderLogManager.java:521)
    at org.apache.juli.ClassLoaderLogManager.readConfiguration(ClassLoaderLogManager.java:464)
    at org.apache.juli.ClassLoaderLogManager.readConfiguration(ClassLoaderLogManager.java:288)
    at java.util.logging.LogManager$2.run(LogManager.java:267) [...]

(This is with JAVA_OPTS=-verbose:class).

I have seen the relevant classes loaded later, but this does not appear to be consistent and is probably an artifact of the aforementioned REST service (which was using it directly).

I can get everything to work correctly if I add the jar to the CLASSPATH directly, but this seems to be generally discouraged as opposed to modifying the loaders.

Is there something particular that I am missing in how to cleanly add a custom java.util.logging.Handler (and, later, a formatter) to the classloader before logging.properties is read?

Alternatively, if I'm flat barking up the wrong tree, I'd take a better approach to the problem of a shared logging configuration between multiple webapps with java.util.logging.

like image 417
David H. Clements Avatar asked Oct 05 '22 04:10

David H. Clements


1 Answers

For the Tomcat-wide custom logging you need to inject your class into the Tomcat bootstrap ClassLoader. Thus jar with custom Handler and required dependencies have to be put into the startup script CLASSPATH. I'd advice to a add custom script at $CATALINA_BASE/bin/setenv.sh, i.e.

#!/bin/sh

CLASSPATH="$CATALINA_BASE/bin/myhandler.jar"

or you can collect required jars dynamically as script variables are loaded during the Tomcat start-up.

like image 164
Valdas Rapševičius Avatar answered Oct 13 '22 10:10

Valdas Rapševičius