Many thanks for these explanations Jacob. Here are some comments. I
have included a modified version of Log4jCRS.
At 00:10 13.12.2002 -0600, Jacob Kjome wrote:
In fact, this version of Log4jCRS is *not* "part of the Container". It is
driven by applications individually. Tomcat needs to know absolutely
nothing about this class for multiple applications to utilize it.
Important point indeed.
Here is how it works...
First, Log4jCRS and log4j.jar need to be in the same classloader. This
can be either WEB-INF/lib or a parent classloader like Tomcat's common/lib
(note that classes in shared/lib count as being in the same classloader as
common/lib). The case of using the WEB-INF/lib kind of defeats the
purpose of using this at all since Log4j already has a unique logging
environment to work within, but it works, nevertheless. What we'll assume
for the following is that log4j.jar and Log4jCRS exist in common/lib
(and/or shared/lib...although log4j.jar needs to be in common/lib or else
Tomcat's commons-logging goes bonkers).
OK.
Each application, at startup, configures Log4j in the init() method of a
servlet or in the contextInitialized() method of a servlet context
listener (the latter being the best choice). Before any call to any
configure() method, the following gets run...
Log4jCRS crs = new w();
crs.initLoggerRepository();
How about just calling Log4jCRS.doIdempotentInitialization() instead?
See also below.
Now call your favorite configure() method.
That's it! A new LoggerRepository has been created uniquely for your
application keyed upon the WebappClassLoader of the current webapp. All
other webapps do the same thing. Again, Tomcat is none-the-wiser and your
code doesn't even know it happened. You set up your loggers and your
configuration just like you always did. You need no special knowledge
about Log4jCRS except just before running your app's configuration.
Yep.
Now you say, "but how do I clean up after logging"? "I have to shut down
appenders when my app shuts down (and the VM/Tomcat continues to run) or
my files will be locked if I am using, for instance, a
FileAppender". Well, Log4jCRS comes to the rescue there as well....
Using the contextDestroyed() method of a servlet context listener, one can
do the following....
ClassLoader cl = Thread.currentThread().getContextClassLoader();
Log4jCRS crs = Log4jCRS.getCRS(cl);
crs.w
crs.remove(cl);
Wouldn't the following accomplish the same?
LogManager.shutdown();
Log4jCRS crs = Log4jCRS.getSingleton();
crs.remove(cl);
That last line is less important since, with that classloader no longer in
existence and the fact that it is stored in a WeakHashMap, it will clean
itself up, eventually. However, since it is convenient, we just do it
ourselves anyway.
To sum up...
We are able to create a unique LoggerRepository for any webapp that wants
one even with log4j existing in the heterogeneous environment of the
parent classloader where, normally, it would be used statically with the
same LoggerRepository for all apps if not for Log4jCRS. And all this is
done without Tomcat having a clue as to the fact that we are using a
custom logger repository selector!
Caveat!!!!
The classes that load Log4jCRS *must* exist in the WebappClassLoader of
*each* webapp. So, if you have a Log4jInit servlet or a servlet context
listener that initializes Log4j, they must be loaded from WEB-INF/lib or
WEB-INF/classes. This is because when Log4jCRS is loaded, it uses the
classloader that loaded it as the key for the logger repositories.
So, Log4jCRS *must* exist in or beside log4j.jar *and* the classes that
load Log4jCRS *must* exist in the WebappClassLoader.
If you can accept that, then I think you will enjoy using Log4jCRS.
There you go. Now you are informed.
Well put but I still don't understand why you need multiple CRS
instances especially since there can be one and only one active
RepositorySelector? Doesn't the Log4jCRS.getSingleton trick do it?
Jake
--
Ceki
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software
* License version 1.1, a copy of which has been included with this
* distribution in the LICENSE.txt file. */
package org.apache.log4j.selectors;
import org.apache.log4j.spi.RepositorySelector;
import org.apache.log4j.spi.LoggerRepository;
import org.apache.log4j.spi.RootCategory;
import org.apache.log4j.Hierarchy;
import org.apache.log4j.Level;
import org.apache.log4j.LogManager;
import java.util.Collections;
import java.util.Map;
import java.util.WeakHashMap;
/**
* @author Jacob Kjome
*/
public class Log4jCRS implements RepositorySelector {
// key: current thread's ContextClassLoader,
// value: Hierarchy instance
final private static Map hierMap = Collections.synchronizedMap(new WeakHashMap());
static Log4jCRS singleton = new Log4jCRS();
static boolean initialized = false;
private Log4jCRS() {}
public static Hierarchy getHierarchy(ClassLoader cl) {
return (Hierarchy) hierMap.get(cl);
}
public LoggerRepository getLoggerRepository() {
ClassLoader cl = Thread.currentThread().getContextClassLoader();
Hierarchy hierarchy = (Hierarchy) hierMap.get(cl);
if(hierarchy == null) {
hierarchy = new Hierarchy(new RootCategory((Level) Level.DEBUG));
hierMap.put(cl, hierarchy);
}
return hierarchy;
}
/**
* The Container should initialize the logger repository for each
* webapp upon startup or reload. In this case, it is controllable
* via each webapp.
*/
public static void doIdempotentInitialization() {
if(!initialized) {
Object guard = new Object();
LogManager.setRepositorySelector(singleton, guard);
initialized = true;
}
}
public static Log4jCRS getSingleton() {
return singleton;
}
public void remove(ClassLoader cl) {
hierMap.remove(cl);
}
}
--
To unsubscribe, e-mail: <mailto:[EMAIL PROTECTED]>
For additional commands, e-mail: <mailto:[EMAIL PROTECTED]>