Daniel Kulp wrote:
[...]
How is this for an idea/comprimise:
For all logging, we use JDK Logger objects. However, instead of calling Logger.getLogger(....), we create a utility class and call: LoggerFactory.getInstance().getLogger(.....). (several varieties of getLogger calls). The default factory just passes them on to JDK Logger getLogger calls. However, if you want, you can replace the LoggerFactory with one that will wrapper log4j Loggers or whatever else you want. The JDK Logger object is not final (or have any final methods) so we can use it as the API for logging, the underlying logging can be just about anything. I COULD be convinced to do something in similar in Celtix as this would JUST affect the Logger creation mechanisms, not any of the actual logging.

What are peoples thoughts about that?

Daniel, sorry for not responding earlier (moved to a new house). First of all, thanks for trying to find a consensus.

My first reaction to the above suggestion was "If it was that simple, then why are people still messing around with jakarta commons-logging, slf4j and friends?" But I wanted to try it out and started to create a prototype of a j.u.l.Logger subclass that delegates to a corresponding log4j Logger. Here are my observations so far:

   * j.u.l.Logger.getLogger() hardcodes "new Logger()" for logger
     creation. We absolutely *must* use our LoggerFactory everywhere or
     things will break. This means that there is a high probability for
     errors, because the proposed design deviates from normal usage of
     the API.
   * If we want to work around that issue, we could implement our own
     LogManager as well. Unfortunately the actual instance is
     controlled by a System property that is evaluated only in the
     static initializer of the LogManager class. This practically means
     it's impossible to control from our code, only from the command
     line, because some low level JDK class might already have logged
     something to j.u.l before our configuration code even runs.
   * I couldn't find a solution that handles j.u.l specific Levels like
     Level.CONFIG which do not exist in log4j. Same with user defined
     levels.
   * The combination of getLevel() and setLevel() is hard to implement.
     I think they should delegate to the log4j logger, but then
     setLevel() followed by getLevel() would not always return the same
     result because FINER and FINEST are both mapped to the log4j TRACE
     level.
   * The implementation of addHandler() is unclear, probably that
     operation should not be supported. Delegating to log4j means that
     j.u.l Handlers will be ignored. This violates the API contract of
     j.u.l.Logger - logging messages are no longer forwarded to
     registered Handlers instead they are forwarded to log4j.

There are probably many more points in the same spirit. To summarize, the design does not follow the substitution principle: the subclass is not a replacement for j.u.l.Logger because many methods of j.u.l.Logger do not make sense for a log4j backend. The only good news is that I managed to get some log messages out of log4j.

To me, the code of the j.u.l.Logger subclass seems like a fragile hack. Yes, I could probably bridge to log4j and get some yoko logging output, but the code will continue to work only if the yoko dev team is extremely disciplined and it is very clear which parts of j.u.l must not be used.

I've also taken a look at j.u.l.Logging package itself. Let me say it politely: I'm still not convinced.

   * throwing() encourages an idiom very similar to the "log and throw"
     style that is discouraged by our coding guidelines
* Naming is horrible. Quick, what's the purpose of Logger.logrb()? * The implementation quality is rather low. Example: Level.equals()
     throws a ClassCastException in the normal code path.

I still think that using our own interface would be the best solution. No external dependencies and maximum flexibility. I could live with slf4j, but it seems that j.u.l is not a viable option for those of us who need to integrate with log4j, even when using a LoggerFactory as suggested above.

Regards, and thanks for reading this rather lengthy email,
Lars

Reply via email to