You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@commons.apache.org by Simon Kitching <sk...@apache.org> on 2006/02/20 00:29:41 UTC

[logging] JCL2.0 design - API

Hi,

I'd like to kick off discussions on what a Jakarta Commons Logging 2.x
release might look like. This thread is on the topic of the JCL API.

Is the current core JCL api ok, or should we change it?

== LogFactory as factory for Log instances

Currently, JCL uses a LogFactory to create Log instances:
  Log log = LogFactory.getLog("fff");

By contrast, Log4j has the factory method on the Log class:   

  Logger log = Logger.getLog("fff");

Having one less class is a *little* nicer for users I think. However it
does reduce the flexibility of the system a little and changing this is
a major difference from JCL 1.x.

My choice would be: keep LogFactory and LogFactory.getLog as the
mechanism for obtaining Log instances.

== LogFactory getAttributeNames/[get|set|remove]Attribute

Currently, "attributes" can be set on a LogFactory instance. Some
attributes control the behaviour of the LogFactory class, some are used
by concrete LogFactory implementations.

I'd be interested in trying to do away with these methods. I don't
believe that a logging bridge like JCL should be configurable from the
application code; that effectively destroys the point of a logging
bridge.

== LogFactory.getFactory

I'd be keen on getting rid of this method completely (at least from the
public API); I don't see why applications should have any need to access
the factory instance.

== LogFactory.getInstance

Calling LogFactory.getFactory().getInstance(param) is equivalent to
LogFactory.getLog(param).

I'd like to do away with the getInstance methods.

== LogFactory release/releaseAll

I would tentatively like to keep release, but change its meaning
somewhat. Currently, it is used to delete any commons-logging-specific
resources, but not clean up the underlying library. There are corner
cases where explicitly releasing resources is still necessary so I think
we need to keep this method. In addition, there are logging libraries
that really need to be explicitly shut down before app exit; I'd like to
expand the LogFactory.release method to have that responsibility. That
would of course need new APIs to the underlying logging-lib-specific
code.

I think the releaseAll method should *definitely* go. It's extremely
dangerous to allow one webapp to release logging resources for all
webapps. Right now, with JCL in a shared classpath, a call to this
method can stuff up logging for all webapps in that container.

== Per-library LogFactory implementation

Originally, JCL had multiple LogFactory subclasses. In later releases
things moved to having just one (LogFactoryImpl) which used reflection
and various other tricks to handle all possible logging
implementations. 

I would like to move back to having a separate LogFactory implementation
per concrete logging library. A bridge to a particular library is
therefore a (LogFactory, Log) implementation pair. A LogFactory
implementation that performs "discovery" could be implemented as a
"decorator" over whatever LogFactory implementation it "discovers".


== Log class methods

Log provides *test* methods and *output* methods:

Log.isDebugEnabled()
Log.debug(msg)
Log.debug(msg, throwable)

I don't see anything wrong with this API, and would like to keep it
exactly as is.

SLF4J uses a different approach for the log message. While JCL users
need to build a string, SLF4j allows parameterised messages:
  slf4jlog.debug("Event {0} occurred at {1}", eventType, eventPlace);

This is nice in some ways; the final message string doesn't need to be
composed until after the logging threshold is tested, so "code guards"
become unnecessary. However it does mean an ugly set of API declarations
where java1.5 varargs aren't supported:
  Log.debug(String template);
  Log.debug(String template, Object arg1);
  Log.debug(String template, Object arg1, Object arg2);
  Log.debug(String template, Object arg1, Object arg2, Object arg3);

A variant like this:
  Log.debug(String template, Object[] args);
is possible, but the creating and building of an Object[] parameter
probably means a "code guard" is now needed anyway.

And finally, all this is *different* from the existing 1.x api. I think
any benefits are marginal, and so would prefer to just stay with the
existing API.

Note that there is a bytecode-modification tool that can be run on code
to automatically insert the necessary JCL code guards into .class files
anyway, so they don't need to be explicitly present in the source if
people prefer not to.


== Package name

Currently, JCL code is in "org.apache.commons.logging". Should we keep
this package name for JCL2 or use something like
"org.apache.commons.logging2"?

For the digester2 project I've been fiddling with, I've been convinced
that "o.a.c.digester2" is the appropriate package name, because the API
has significant changes; the new code is nowhere near a "drop in
replacement" for the prior version.

However in this case, if we agree to keep the Log API intact, and only
remove rarely-used methods from the LogFactory interface then the result
will be that 99% or more of existing code that uses JCL1.x could use
JCL2.x without even needing recompilation. I'd therefore suggest that we
keep the same package name.

== Final Notes

One alternative to removing methods from the LogFactory interface is
instead to define them as no-ops, or to have them throw
UnsupportedOperationException. The cost is some extra baggage, but we
could put clear comments saying that all implementations of LogFactory
should simply ignore the method. It might be worth doing this for the
sake of backwards compatibility...


Cheers,

Simon


---------------------------------------------------------------------
To unsubscribe, e-mail: commons-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: commons-dev-help@jakarta.apache.org


Re: [logging] JCL2.0 design - API

Posted by "V. Karthik Kumar" <ka...@gmail.com>.
Hi,

Could we defer the mechanism of classloading to static or dynamic 
loaders, to make it comply with the existing architecture as well? Isn't 
that simpler?


Simon Kitching wrote:
> Hi,
>
> I'd like to kick off discussions on what a Jakarta Commons Logging 2.x
> release might look like. This thread is on the topic of the JCL API.
>
> Is the current core JCL api ok, or should we change it?
>
> == LogFactory as factory for Log instances
>
> Currently, JCL uses a LogFactory to create Log instances:
>   Log log = LogFactory.getLog("fff");
>
> By contrast, Log4j has the factory method on the Log class:   
>
>   Logger log = Logger.getLog("fff");
>
> Having one less class is a *little* nicer for users I think. However it
> does reduce the flexibility of the system a little and changing this is
> a major difference from JCL 1.x.
>
> My choice would be: keep LogFactory and LogFactory.getLog as the
> mechanism for obtaining Log instances.
>
> == LogFactory getAttributeNames/[get|set|remove]Attribute
>
> Currently, "attributes" can be set on a LogFactory instance. Some
> attributes control the behaviour of the LogFactory class, some are used
> by concrete LogFactory implementations.
>
> I'd be interested in trying to do away with these methods. I don't
> believe that a logging bridge like JCL should be configurable from the
> application code; that effectively destroys the point of a logging
> bridge.
>
> == LogFactory.getFactory
>
> I'd be keen on getting rid of this method completely (at least from the
> public API); I don't see why applications should have any need to access
> the factory instance.
>
> == LogFactory.getInstance
>
> Calling LogFactory.getFactory().getInstance(param) is equivalent to
> LogFactory.getLog(param).
>
> I'd like to do away with the getInstance methods.
>
> == LogFactory release/releaseAll
>
> I would tentatively like to keep release, but change its meaning
> somewhat. Currently, it is used to delete any commons-logging-specific
> resources, but not clean up the underlying library. There are corner
> cases where explicitly releasing resources is still necessary so I think
> we need to keep this method. In addition, there are logging libraries
> that really need to be explicitly shut down before app exit; I'd like to
> expand the LogFactory.release method to have that responsibility. That
> would of course need new APIs to the underlying logging-lib-specific
> code.
>
> I think the releaseAll method should *definitely* go. It's extremely
> dangerous to allow one webapp to release logging resources for all
> webapps. Right now, with JCL in a shared classpath, a call to this
> method can stuff up logging for all webapps in that container.
>
> == Per-library LogFactory implementation
>
> Originally, JCL had multiple LogFactory subclasses. In later releases
> things moved to having just one (LogFactoryImpl) which used reflection
> and various other tricks to handle all possible logging
> implementations. 
>
> I would like to move back to having a separate LogFactory implementation
> per concrete logging library. A bridge to a particular library is
> therefore a (LogFactory, Log) implementation pair. A LogFactory
> implementation that performs "discovery" could be implemented as a
> "decorator" over whatever LogFactory implementation it "discovers".
>
>
> == Log class methods
>
> Log provides *test* methods and *output* methods:
>
> Log.isDebugEnabled()
> Log.debug(msg)
> Log.debug(msg, throwable)
>
> I don't see anything wrong with this API, and would like to keep it
> exactly as is.
>
> SLF4J uses a different approach for the log message. While JCL users
> need to build a string, SLF4j allows parameterised messages:
>   slf4jlog.debug("Event {0} occurred at {1}", eventType, eventPlace);
>
> This is nice in some ways; the final message string doesn't need to be
> composed until after the logging threshold is tested, so "code guards"
> become unnecessary. However it does mean an ugly set of API declarations
> where java1.5 varargs aren't supported:
>   Log.debug(String template);
>   Log.debug(String template, Object arg1);
>   Log.debug(String template, Object arg1, Object arg2);
>   Log.debug(String template, Object arg1, Object arg2, Object arg3);
>
> A variant like this:
>   Log.debug(String template, Object[] args);
> is possible, but the creating and building of an Object[] parameter
> probably means a "code guard" is now needed anyway.
>
> And finally, all this is *different* from the existing 1.x api. I think
> any benefits are marginal, and so would prefer to just stay with the
> existing API.
>
> Note that there is a bytecode-modification tool that can be run on code
> to automatically insert the necessary JCL code guards into .class files
> anyway, so they don't need to be explicitly present in the source if
> people prefer not to.
>
>
> == Package name
>
> Currently, JCL code is in "org.apache.commons.logging". Should we keep
> this package name for JCL2 or use something like
> "org.apache.commons.logging2"?
>
> For the digester2 project I've been fiddling with, I've been convinced
> that "o.a.c.digester2" is the appropriate package name, because the API
> has significant changes; the new code is nowhere near a "drop in
> replacement" for the prior version.
>
> However in this case, if we agree to keep the Log API intact, and only
> remove rarely-used methods from the LogFactory interface then the result
> will be that 99% or more of existing code that uses JCL1.x could use
> JCL2.x without even needing recompilation. I'd therefore suggest that we
> keep the same package name.
>
> == Final Notes
>
> One alternative to removing methods from the LogFactory interface is
> instead to define them as no-ops, or to have them throw
> UnsupportedOperationException. The cost is some extra baggage, but we
> could put clear comments saying that all implementations of LogFactory
> should simply ignore the method. It might be worth doing this for the
> sake of backwards compatibility...
>
>
> Cheers,
>
> Simon
>
>
>   


---------------------------------------------------------------------
To unsubscribe, e-mail: commons-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: commons-dev-help@jakarta.apache.org


Re: [logging] JCL2.0 design - API

Posted by Boris Unckel <bo...@gmx.net>.
> Von: Emmanuel Bourg <eb...@apache.org>
> Boris Unckel wrote:
> > To turn Log into an abstract class means that no native implementation
> will
> > ever be possible. It would definitly mean that you will need an wrapper
> > class. Currently there is just x4juli supporting native JCL support for
> > java.util.logging (X4JuliLogger extends jul.Logger implements JCL).
> > But this is also a chance for log4j2.
> 
> What do you mean by "native" implementation ? Do you mean a multiple 
> inheritance to implement the Log type from JCL and the Logger type from 
> java.util.logging ?
Yes:
X4JuliLogger extends jul.Logger implements JCL

Have a look
at:
http://svn.berlios.de/viewcvs/x4juli/trunk/src/java/org/x4juli/X4JuliLogger.java?view=markup

Regards
Boris

---------------------------------------------------------------------
To unsubscribe, e-mail: commons-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: commons-dev-help@jakarta.apache.org


Re: [logging] JCL2.0 design - API

Posted by Emmanuel Bourg <eb...@apache.org>.
Boris Unckel wrote:
> To turn Log into an abstract class means that no native implementation will
> ever be possible. It would definitly mean that you will need an wrapper
> class. Currently there is just x4juli supporting native JCL support for
> java.util.logging (X4JuliLogger extends jul.Logger implements JCL).
> But this is also a chance for log4j2.

What do you mean by "native" implementation ? Do you mean a multiple 
inheritance to implement the Log type from JCL and the Logger type from 
java.util.logging ?

Emmanuel Bourg

---------------------------------------------------------------------
To unsubscribe, e-mail: commons-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: commons-dev-help@jakarta.apache.org


Re: [logging] JCL2.0 design - API

Posted by Boris Unckel <bo...@gmx.net>.
Hello,

> Von: Emmanuel Bourg <eb...@apache.org>
> I just wanted to mention this solution if Log had to remain an 
> interface, but I won't support it. I prefer to turn Log into an abstract 
> class.

To turn Log into an abstract class means that no native implementation will
ever be possible. It would definitly mean that you will need an wrapper
class. Currently there is just x4juli supporting native JCL support for
java.util.logging (X4JuliLogger extends jul.Logger implements JCL).
But this is also a chance for log4j2.

The wrapper class approach has _heavy_ performance issues (Up to x-hundred
percent). I am working on some benchmarking for all established loggers, but
I have to check the code, provide some good ant skript before publishing.

Regards
Boris

---------------------------------------------------------------------
To unsubscribe, e-mail: commons-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: commons-dev-help@jakarta.apache.org


Re: [logging] JCL2.0 design - API

Posted by Emmanuel Bourg <eb...@apache.org>.
Simon Kitching wrote:
> Fascinating! I haven't seen that trick before. An interface containing a
> static method (via a little indirection)...cool.
> 
> Are you serious about this proposed API for the Log class, though?
> 
> I'm hoping that people will agree that the current Log API is ok (even
> if it isn't 100% perfect), as that makes 2.x more backwards-compatible.
> However if a change is made, I think it would make more sense to move to
> the j.u.l/log4j solution (Log is abstract class) rather than invent yet
> another design. However if you think 
>   Log.Factory.getLog("foo")
> really should be considered then we can start debating pros/cons.

I just wanted to mention this solution if Log had to remain an 
interface, but I won't support it. I prefer to turn Log into an abstract 
class.

Emmanuel Bourg


---------------------------------------------------------------------
To unsubscribe, e-mail: commons-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: commons-dev-help@jakarta.apache.org


Re: [logging] JCL2.0 design - API

Posted by Simon Kitching <sk...@apache.org>.
On Wed, 2006-03-01 at 18:22 +0100, Emmanuel Bourg wrote:
> There is no variable, this would be implemented with a static inner 
> class in the Log interface, something like this:
> 
> public interface Log {
> 
>      ...
> 
>      public static class Factory {
>          public static Log getLog(String name) {
>              ...
>          }
>      }
> }

Fascinating! I haven't seen that trick before. An interface containing a
static method (via a little indirection)...cool.

Are you serious about this proposed API for the Log class, though?

I'm hoping that people will agree that the current Log API is ok (even
if it isn't 100% perfect), as that makes 2.x more backwards-compatible.
However if a change is made, I think it would make more sense to move to
the j.u.l/log4j solution (Log is abstract class) rather than invent yet
another design. However if you think 
  Log.Factory.getLog("foo")
really should be considered then we can start debating pros/cons.

PS: Craig, I agree completely with your miminal LogFactory abstract
class which has just the minimal possible implementations for
getInstance and getLog. All that map-of-factory-by-classloader stuff can
be pushed down into a subclass.....

Cheers,

Simon



---------------------------------------------------------------------
To unsubscribe, e-mail: commons-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: commons-dev-help@jakarta.apache.org


Re: [logging] JCL2.0 design - API

Posted by Emmanuel Bourg <eb...@apache.org>.
Craig McClanahan wrote:

>> It's possible to keep a Log interface and having one less class to
>> import by adding a static class in the Log interface. It would look like
>> this:
>>
>>      Log log = Log.Factory.getLog("foo");
>>
> 
> I presume this would be implemented by making "Factory" a static variable?
> That doesn't offer much protection against malicious applications trying to
> replace it and potentially messing up other users.

There is no variable, this would be implemented with a static inner 
class in the Log interface, something like this:

public interface Log {

     ...

     public static class Factory {
         public static Log getLog(String name) {
             ...
         }
     }
}

Emmanuel Bourg


---------------------------------------------------------------------
To unsubscribe, e-mail: commons-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: commons-dev-help@jakarta.apache.org


Re: [logging] JCL2.0 design - API

Posted by Craig McClanahan <cr...@apache.org>.
On 3/1/06, Emmanuel Bourg <eb...@apache.org> wrote:
>
> Boris Unckel wrote:
> > Hello,
> >
> > Simon Kitching wrote:
> >> == LogFactory as factory for Log instances
> >>
> >> Currently, JCL uses a LogFactory to create Log instances:
> >>   Log log = LogFactory.getLog("fff");
> >>
> >> By contrast, Log4j has the factory method on the Log class:
> >>   Logger log = Logger.getLog("fff");
> >>
> >> Having one less class is a *little* nicer for users I think. However it
> >> does reduce the flexibility of the system a little and changing this is
> >> a major difference from JCL 1.x.
> >>
> >> My choice would be: keep LogFactory and LogFactory.getLog as the
> >> mechanism for obtaining Log instances.
> > There is one really big advantage of the current API in this case:
> > If you want to have Logger.getLog("fff") Logger must be a class,
> > currently it is an interface.
> > Please do not switch from an interface to an class, since it is
> > impossible to provide a native
> > implementation of JCL2 without an interface.
>
> It's possible to keep a Log interface and having one less class to
> import by adding a static class in the Log interface. It would look like
> this:
>
>      Log log = Log.Factory.getLog("foo");
>
> I don't expect everyone to like it ;) I personally prefer the
> Log4j/JDK1.4 syntax.


I presume this would be implemented by making "Factory" a static variable?
That doesn't offer much protection against malicious applications trying to
replace it and potentially messing up other users.

The place where we botched LogFactory most in 1.0 was that there's too much
logic in the static method, and you cannot easily provide your own factory
implementation that might operate differently.  In Commons Chain, which has
a similar need for a pluggable factory, we followed a pattern like this:

    public abstract class CatalogFactory {

        // Return the configured instance of the factory to use for this app
        public static CatalogFactory getInstance();

    }

where the actual instance returned by getInstance() is configured by some
suitable discovery mechanism.

Translating this to LogFactory terminology, it might look like:

    public abstract class LogFactory {

        public static LogFactory getInstance();

    }

and you would want users to call:

    Log log = LogFactory.getInstance().getLog("...");

but to maintain reasonable backwards compatibility, you could also implement
getLog() in LogFactory like this:

    public static Log getLog(String name) {
        return getInstance().getLog(name);
    }

(and similarly for the version that takes a Class).

The tricky bit is still in how you configure the instance to be returned by
LogFactory.getInstance(), but at least we could provide a pluggability point
for containers that wanted to do something special.

Craig

Emmanuel Bourg
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: commons-dev-unsubscribe@jakarta.apache.org
> For additional commands, e-mail: commons-dev-help@jakarta.apache.org
>
>

Re: [logging] JCL2.0 design - API

Posted by Emmanuel Bourg <eb...@apache.org>.
Boris Unckel wrote:
> Hello,
> 
> Simon Kitching wrote:
>> == LogFactory as factory for Log instances
>>
>> Currently, JCL uses a LogFactory to create Log instances:
>>   Log log = LogFactory.getLog("fff");
>>
>> By contrast, Log4j has the factory method on the Log class:  
>>   Logger log = Logger.getLog("fff");
>>
>> Having one less class is a *little* nicer for users I think. However it
>> does reduce the flexibility of the system a little and changing this is
>> a major difference from JCL 1.x.
>>
>> My choice would be: keep LogFactory and LogFactory.getLog as the
>> mechanism for obtaining Log instances.
> There is one really big advantage of the current API in this case:
> If you want to have Logger.getLog("fff") Logger must be a class, 
> currently it is an interface.
> Please do not switch from an interface to an class, since it is 
> impossible to provide a native
> implementation of JCL2 without an interface.

It's possible to keep a Log interface and having one less class to 
import by adding a static class in the Log interface. It would look like 
this:

     Log log = Log.Factory.getLog("foo");

I don't expect everyone to like it ;) I personally prefer the 
Log4j/JDK1.4 syntax.

Emmanuel Bourg

---------------------------------------------------------------------
To unsubscribe, e-mail: commons-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: commons-dev-help@jakarta.apache.org


Re: [logging] JCL2.0 design - API

Posted by Boris Unckel <bo...@gmx.net>.
Hello,

Simon Kitching wrote:
> == LogFactory as factory for Log instances
>
> Currently, JCL uses a LogFactory to create Log instances:
>   Log log = LogFactory.getLog("fff");
>
> By contrast, Log4j has the factory method on the Log class:   
>
>   Logger log = Logger.getLog("fff");
>
> Having one less class is a *little* nicer for users I think. However it
> does reduce the flexibility of the system a little and changing this is
> a major difference from JCL 1.x.
>
> My choice would be: keep LogFactory and LogFactory.getLog as the
> mechanism for obtaining Log instances.
There is one really big advantage of the current API in this case:
If you want to have Logger.getLog("fff") Logger must be a class, 
currently it is an interface.
Please do not switch from an interface to an class, since it is 
impossible to provide a native
implementation of JCL2 without an interface.

Regards
Boris


---------------------------------------------------------------------
To unsubscribe, e-mail: commons-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: commons-dev-help@jakarta.apache.org