Hi peeps,
jumping in a little late here I guess....I'm going to mostly rant about
how avalon relates to (and could in the future) commons-logging and
logging in general, and not about the general case, as I feel I don't
have enough knowledge to talk about the general case.
Summary
=======
warning: terribly long-winded! :D
We are also looking at how to integrate logkit & log4j; good hopes too
but (personal estimate) it will take lots of time and effort.
We are looking at how to integrate commons-logging into avalon; it looks
promising. We cannot drop stuff like the Logger interface in
avalon-framework because we need backwards compatibility.
It seems like a commons-logging-noop.jar would make the difficulty of
integrating a commons-logging-using-library into an avalon container and
similar projects mostly dissapear.
(I also take too much time to explain the current logging setup in the
various parts of avalon)
Logging in Avalon
=================
Introduction
------------
There's three main logging-related packages over @ avalon. There's
avalon-logkit, which is a seperate downloadable binary that has got no
dependency on any other part of avalon, and vice versa other parts of
avalon have mostly been modified to have no more than soft dependencies
on avalon-logkit.
The avalon-logkit feature set and use case is about the same as for
log4j (differences are becoming less and less with each release).
There's excalibur-logger, which is mostly utility code for use by avalon
containers to handle their logging setup. It is distributed as part of
phoenix, ECM, and also included in the old big releases of excalibur.
This is sort-of avalon-internal utility code.
Then there's avalon-framework, the client API which avalon components
talk to in order to handle their logging. There's a package
org.apache.avalon.framework.logger, with the avalon lifecycle interface
LogEnabled, into which is passed an implementation of Logger. There's
implementations of that Logger interface using the console, logkit,
log4j and jdk14 logging.
This package is very similar to commons-logging in that it provides a
sort-of facade to an underlying logging implementation.
Logkit & Log4J
--------------
There's been talks for ages about increased synergy between these
projects, and recently people have been getting more enthousiastic
again. It's doable. Hopefully we'll get there sometime. Lack of time and
the need to support an existing userbase will mean this kind of process
will probably take lots of time. And, I personally find this kind of
stuff a terribly unsexy task ;)
(sidenote: I think it makes as little sense to integrate logkit into
commons-logging as it makes sense to integrate log4j into
commons-logging, ie very little. Commons-logging should not provide a
logging implementation IMHO.)
Avalon-Framework & Commons-Logging
----------------------------------
We used to have (still have, deprecated) a Loggable interface which was
coupled to Logkit, ie:
public interface Loggable {
void setLogger( org.apache.log.Logger logger ); }
this was cumbersome as lots of people wanted to use log4j with avalon
for obvious reasons, hence we now have LogEnabled and a simple facade.
Looking at the similarity between the Logger interface in
avalon-framework and the Logger interface in commons-logging it is
obvious that it makes sense to remove one in place of the other. It does
not make sense to have a dependency on avalon-framework for commons-logging.
While it can make sense to have a dependency on commons-logging for
avalon-framework, we are not going to change the LogEnabled interface
(in avalon-framework 4.x) because we need total binary compatibility. As
such, we also need to keep supporting the Logger interface in
avalon-framework.
What is probably perfectly doable is providing:
--------------------------------
package org.apache.avalon.framework;
public class CommonsLoggingLogger implements Logger
{
private Log m_log;
public CommonsLoggingLogger(
org.apache.commons.loggging.Log log )
{ m_log = log; }
void debug( String message ) { m_log.debug( message ); }
// simply redirect all methods defined in Logger to Log
}
--------------------------------
this is on the TODO. Not for an upcoming avalon-framework 4.1.4, but it
might very well be in 4.1.5.
Excalibur-Logger & Commons-Logging
----------------------------------
Avalon containers like Avalon-Phoenix provide a very rigid black-box to
hosted components, and provide support for complex multi-classloader
setups. Very similar tech and associated problems as in EJB and servlet
containers.
Within such a setup, the static factory pattern can often lead to nasty
problems most of y'all probably have more experience with than me. We're
very cautious with promoting use of libraries in avalon client code
which enable use of static factories (the use of static factories within
an avalon-container itself is less of an issue, as using IoC means the
container behaviour in such a situation can be deterministic, but as a
matter of habit, preference, and example, avalon containers are pretty
non-static as well).
These containers defer to excalibur-logger for handling most of their
logging needs. Excalibur-logger contains an interface LoggerManager,
which does mostly the same as commons-logging its LogFactory, with the
main difference it is completely non-static in nature. LoggerManager
also needs to stay around for ages in order to preserve backwards
compatibility.
Again, it should be possible to write a CommonsLoggerManager which
defers to a LogFactory:
--------------------------------
package org.apache.avalon.excalibur.logger;
public class CommonsLoggerManager implements LoggerManager
{
private org.apache.commons.LogFactory m_factory;
public CommonsLoggingLogger(
org.apache.commons.loggging.LogFactory factory )
{ m_factory = factory; }
Logger getLoggerForCategory( String name )
{
return new CommonsLoggingLogger( m_factory.getInstance(name) );
}
// simply redirect all methods defined in LoggerManager
// to LogFactory
}
--------------------------------
investigating this and how well it works is also on the TODO. I believe
Jason's already done a lot of work in this direction with plexus, but I
haven't actually looked at it yet.
Logging in libraries used by Avalon
===================================
The dillema/problem
-------------------
Stuff like commons-collections, commons-cli and commons-lang and all
those other libraries I don't know much about just yet can definately be
put to use in avalon all over the place. Our goal is indeed to get the
'utility code' in avalon 'out' (and donate it to another project or
deprecate it if a better alternative supporting the full usecase already
exists, like with cli), and use the common code as much as possible.
In avalon containers like Avalon-Phoenix, we need to provide the end
user with control over how to do logging. If a library does something like
class MyToolLikeCLI
{
private static Log log = LogFactoryImpl.getInstance("");
doStuff()
{
try { /* ... */ } catch( Throwable t )
{ log.error( "no good" ); }
}
}
that makes our job a lot harder. Hence
<dependency><id>commons-logging</id></dependency>
triggers a warning sign when evaluating how easy it is to integrate a
library, as 'LogFactoryImpl.getInstance("")' looks like a lot of work to
circumvent cleanly, and it is I believe commonly used.
Note that I totally agree the code in avalon creates a lot of the same
problem with possibility for stuff like
private static Logger log = new ConsoleLogger()
The only difference is that such a setup is discouraged in avalon and
not in commons-logging. It'd be nice if the commons-logging docs would
as well or at least point out potential problems (says nosy me, not
actually knowing whether they do).
Personal preferences 'n stuff
-----------------------------
I like a library that doesn't use anything with a 'static' keyword
attached to any of its methods that actually do much. It just makes life
easier in the classloader-hell world we have in java. I would like the
commons libraries to use a similar setup to Digester's setLogger() as
much as possible.
Possible solution/workaround
----------------------------
Now, I saw someone suggest this problem is avoidable, ie by putting in
place an alternative implementation for commons-logging which intercepts
stuff like getInstance(), perhaps always providing a NoOpLog. This
sounds like a workable, easily implementable idea, even if it feels
'hacky'. IIUC what would happen is we put in place
commons-logging-noop.jar (commons-stub.jar)
commons-cli.jar
commons-lang.jar
commons-(...).jar
and never again get anxious about
<dependency><id>commons-logging</id></dependency>
Sounds like a plan, and probably a rather common need for many projects,
not just avalon. Maybe some smart classloader hacker/proxy wizard can
even create something that properly routes LogFactory calls back into an
excalibur LoggerManager...but I see a bigger amount of work and
complexity involved there.
hope this all made some kind of sense to someone somewhere.
cheers & g'night,
- Leo
---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]
- Re: [logging] To depend or not to depend? Leo Simons
- Re: [logging] To depend or not to depend? Stephen Colebourne
