[slf4j-dev] [Bug 31] Varargs for Logger methods

bugzilla-daemon at pixie.qos.ch bugzilla-daemon at pixie.qos.ch
Tue Nov 10 11:50:07 CET 2009


http://bugzilla.slf4j.org/show_bug.cgi?id=31





--- Comment #45 from Joern Huxhorn <joern at huxhorn.de>  2009-11-10 11:50:06 ---
I've given redesigning SLF4J for JDK>=1.5 a try some time ago. 

This is a concept at the moment and could be enhanced. I'd volunteer to do just
that if this idea is approved.

Please take a look at the code at the following URL:
http://lilith.git.sourceforge.net/git/gitweb.cgi?p=lilith/lilith;a=tree;f=slf4j/src/main/java/de/huxhorn/lilith/slf4j;hb=HEAD

Let me explain the idea behind the code...

Facts:
1.) we must not change the original SLF4J API to prevent breakage in modules
that are currently using it.
2.) we want to to use varargs.
3.) we want to simplify the API, if possible.
4.) we'd like to be able to use parameterized logging in combination with
Throwable ( http://bugzilla.slf4j.org/show_bug.cgi?id=70 )

4.) is necessary to be able to provide 3.) - meaning that logger methods are
reduced to the minimum. 

My solution proposal works like this:
- we leave org.slf4j.Logger and org.slf4j.LoggerFactory exactly like they are.
This is necessary to solve 1.)
- we define a new Logger interface and a new LoggerFactory in a different
package. In my proposal this is de.huxhorn.lilith.slf4j but this would be
something reasonable instead, like org.slf4j.jdk15 or org.slf4jng or whatever
some creative mind invents here. I'll use "n" as the package name for the
remainder of this document.
Using a different package instead of different class name reduces the amount of
change necessary to switch from old to new classes. One would simply change the
import.
- n.Logger isn't binary compatible to Logger but compile-compatible - no
further changes beside import are necessary.
- n.LoggerFactory returns an n.Logger, using LoggerFactory to resolve the
original Logger.
- if the Logger returned by LoggerFactory already implements n.Logger it is
simply cast and returned (not yet implemented that way), otherwise it is
wrapped. There should also be some caching of wrapped loggers (not yet
implemented).
- n.Logger looks like this (some methods omitted):

public interface Logger
{
        /**
         * The level of an event.
         */
        enum Level
        {
                TRACE, DEBUG, INFO, WARN, ERROR
        }

        /**
         * The threshold of a logger.
         */
        enum Threshold
        {
                ALL(Level.TRACE), TRACE(Level.TRACE), DEBUG(Level.DEBUG),
INFO(Level.INFO), WARN(Level.WARN), ERROR(Level.ERROR), OFF(null);

                private final Level level;

                Threshold(Level level)
                {
                        this.level = level;
                }

                public boolean passes(Level level)
                {
                        return this.level != null &&
this.level.compareTo(level) <= 0;
                }
        }

        /**
         * @return the Threshold of this Logger.
         */
        Threshold getThreshold();

        // generic logging methods
        // Those are missing really badly from SLF4J right now
        // - which is good since we wouldn't have been able
        // to switch to enum so easily otherwise ;)
        boolean isLoggingEnabled(Level level);

        boolean isLoggingEnabled(Level level, Marker marker);

        void log(Level level, String messagePattern, Object... args);

        void log(Level level, Marker marker, String messagePattern, Object...
args);

        // TRACE methods
        boolean isTraceEnabled();

        boolean isTraceEnabled(Marker marker);

        void trace(String messagePattern, Object... args);

        void trace(Marker marker, String messagePattern, Object... args);

        // same signatures for other Levels, i.e. debug, info, warn, error
}

This takes care of 2.) and 3.). We only have 4 methods per log-level instead of
12.

- If the last argument of the message is a Throwable and is not used up as a
parameter in the messagePattern it is set as the Throwable of the LoggingEvent.
This is done by some other code of mine, my own MessageFormatter, that I'd also
happily donate. This takes care of 4.)

This means that developers need to explicitly opt-in if they want to use the
n.Logger interface, preventing any headaches that would be caused by extending
the original interface. The additional wrapper will vanish as soon as SLF4J
implementations like Logback are implementing the n.Logger interface natively.

I'm really happy with this solution so far since I fail to find any downsides.

Please let me know what you think. I'd start (re)implementing this for
inclusion in SLF4J as soon as I get a go from Ceki.
I'd put those classes into a whole new artifact like slf4j-jdk15-api or
slf4jng-api or whatever. (I'm not terribly good concerning names ;)) which
would then also include my new MessageFormatter, which is thoroughly tested.


-- 
Configure bugmail: http://bugzilla.slf4j.org/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
You are the assignee for the bug.



More information about the slf4j-dev mailing list