Thanks Simon,

Simon Kitching schrieb:

On Wed, 2005-05-18 at 12:20 +0200, David Perez wrote:


You're right, I have changed the commons-logging version to the latest one.
Now I have reverted to commons-logging-1.0.2



I would recommend using commons-logging-1.0.4. After all, that is the most recent official release.



I have already done so successfully.  See below for more details.



I suggest registering a ServletContextListener. Declare a <listener>
element in the web.xml file, with a nested <listener-class> element
containing the fully qualified classname of a logging-cleanup class. In
the contextDestroyed method of that class do this:
* LogFactory.release(
   Thread.currentThread().getContextClassLoader())
* somehow cleaning up the underlying logging library if needed.

If that doesn't help, let me know and we can look into it a bit
further.





From my code I call LogFactory.releaseAll() after removing the old web app context, and no luck :-(



Do you mean that you use a ServletContextListener as I described? Or something else?

Note also that LogFactory.releaseAll may not do what you want. If your
webapp has bound to commons-logging that is loaded via a shared
classloader, then calling releaseAll will shut down *all* instances of
commons-logging - which other webapps running in the same container may
not appreciate :-). The code I recommended will only shut down logging
for the webapp whose context is being destroyed, assuming you are
calling the method from a SerletContextListener.contextDestroyed, ie
from code where Thread.currentThread.getContextClassLoader() returns the
classloader of the webapp being unloaded.



Now I use LogFactory.release()

Should my webapp use another copy of commons-logging.jar?



That depends.

If your webapp has all the libraries it needs in WEB-INF/lib, then yes.
Deploying commons-logging.jar in WEB-INF/lib in that situation makes
things very clean and consistent. There's no need to use call
LogFactory.release() in this case, as undeploying the webapp
automatically undeploys commons-logging and the underlying logging lib.

But if your webapp is calling into code that is deployed via a shared
classloader, and that code uses commons-logging, then you must *not*
also deploy commons-logging in WEB-INF/lib. If you do (and you are using
release 1.0.4), you will get the message
Invalid class loader hierarchy. You have more than one version of org.apache.commons.logging.Log
visible, which is not allowed.
If you are using an earlier release, you will probably get something
like:
AvalonLogger does not implement Log





Finally I use a shared instance.



Here is the logging jars I use in my app:

jars used by the web server (Jetty):

   * commons-logging.jar and
   * log4j.jar

jars used by the webapp:

   *    logkit.jar

My web app uses Cocoon which uses Avalon LogKit, and makes me include logkit.jar, even though logging is redirected to log4j.


What do you mean "logging is redirected to log4j"?



Avalon LogKit can be configured to use the Log4J subsystem. I like more Log4J than Avalon LogKit.

   Some other classes use directly commons-logging

With commons-logging-1.0.4 and 1.05 alpha1, I have to make logkit.jar available to Jetty, otherwise commons-loggings throws a ClassNotFoundException, because it cannot find

org/apache/log/Hierarchy. I wonder why it tries to load LogKit if Log4j is already available?



Commons-logging does *not* auto-discover LogKit; it doesn't even try. So
somewhere something in your webapp must be *ordering* commons-logging to
use AvalonLogger (aka LogKit) as the logger.


Which logging lib commons-logging uses can be explicitly set via a
number of methods; .properties files, jdk1.3 service files, system
properties. See the commons-logging documentation for more information.



You were right, I had a WEB-INF/classes/common-logging.properties file, of my webapp, that I used to use before I decided to use log4j instead of LogKit. Commons-loggind 1.02 did ignore this file, but version 1.0.4 didn't. This what has confused me.



Caused by: java.lang.NoClassDefFoundError: org/apache/log/Hierarchy
at org.apache.commons.logging.impl.LogKitLogger.getLogger(LogKitLogger.java:77)
at org.apache.commons.logging.impl.LogKitLogger.<init>(LogKitLogger.java:64)
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:39)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:27)
at java.lang.reflect.Constructor.newInstance(Constructor.java:494)
at org.apache.commons.logging.impl.LogFactoryImpl.newInstance(LogFactoryImpl.java:529)



Yep, commons-logging's LogFactoryImpl is explicitly trying to
instantiate a LogKitLogger object.




Can you please:
(a) look for what is ordering commons-logging to use LogKit and remove
that?
(b) use commons-logging 1.0.4; I will be able to help you much better
than if you use the very old 1.0.2.


Also, can you please confirm that Jetty (and your webapp) use the
standard child-first (aka parent-last) classloading order? I know the
Resin webserver doesn't, and that makes things quite different...


And by the way, where do you put your log4j configuration file?





I load it programmatically with a call to
PropertyConfigurator.configureAndWatch(new File(basedir, "logs/"+System.getProperty("loggging.file", "log-production.properties")).toString());
in this way I can


  1. change dynamically the logging configuration
  2. have two sets of logging configurations: one for production (the
     default) and other for development (overriden through a system
     property in the debug launch). In this way, increasing log level
     for testing purposes never goes to production inadvertently.

Good luck!


Now I have only problems with log4j and some other static instances, but not with commons-logging!!
Thanks for your invaluable help.


David

Reply via email to