[slf4j-dev] Consolidating the LoggerFactory / A better plugin mechanism

Eric Crahen eric.crahen.lists at gmail.com
Sat Feb 10 00:14:03 CET 2007


I've been using the SLF4J API in some of my work and one thing about it that
makes it very awkward, from a dependency mangement standpoint, is that one
of the core APIs - the LoggerFactory - is actually not a part of the
slf4j-api package. Instead, its actually reimplemented in each of the slf4j
implementations. This is awkward for various reasons,

1. The API is incomplete. I actually can't build anything against the API
since I really do require a LoggerFactory; I need to choose an arbitrary
implementation to build against.
2. It complicates deployments in some situations where
compile-time-dependencies + run-time-dependencies because of #1.
4. All of the LoggerFactory implementations are basically identical
3. I have to hope everyone uses the same LoggerFactory signature.

The nice part is, that with the mechanism SLF4J has, selecting a Logger
implementation ss purely a deployment issue. Just deploy the right jar and
the Logger you pick takes effect. That's very handy.

I think that we can resolve the undesirable issues while still retaining the
behavior we have today. Here is my proposal:

Create a single LoggerFactory and placed it into slf4j-api, and deleted the
LoggerFactory classes everywhere else they exist (slf4j-simple, logback,
etc). This new LoggerFactory leverages Sun's ServiceProvider API which
exists in all JDK's since 1.4 (maybe 1.3). It looks like this:

package org.slf4j;

import sun.misc.Service;
import org.slf4j.impl.Util;
import java.util.Iterator;

/**
 * This class is a simple wrapper over Sun's Service provider
 * factory. It provides a standard mechanism for loading plugins
 * in a simple way.
 */
public abstract class LoggerFactory {

  private static final ILoggerFactory factory;

  // Initialize the real ILoggerFactory
  static {

    ILoggerFactory f = null;
    try {

      // Use the current context ClassLoader to enumerate all the resources
      // that define ILoggerFactory services.
      for(Iterator i = Service.providers(ILoggerFactory.class); i.hasNext();
) {
        Object o = i.next();
        if(f == null)
          f = (ILoggerFactory)o;
        else
          Util.reportWarning("More than once service provider was defined");
      }

    factory = f;
    if(f == null) {
      // TODO: Should there be a simple fall back on a System.out logger?
      // TODO: Perhaps the simple loggers is included with SLF4J-API and
      // TODO: Is selected always, unless another implementation is provided
    }

  }

  public static Logger getLogger(Class name) {
    return getLogger(name.getName());
  }

  public static Logger getLogger(String name) {
    if(factory != null)
      return factory.getLogger(name);
    // TODO: Not a problem if the above suggestion is taken
    throw new IllegalStateException("No " + ILoggerFactory.class + "
implementation was provided!");
  }

}

The simple explanation of this is that all the magic happens in the Service
class. What it does, for those who are unfamilair, is that it uses the
context ClassLoader to locate a resource named services/<<fully qualified
Interface name>>. This resource can contain multiple lines of text, each
line should be the fully qualified class name of a concrete implementation
of the interface you asked for. This class should have a default
constructor.

To give an example of this simple class can be used in place of what we have
now I'll walk through an example of how I refitted the slf4j-simple package
to utilize this,

Step #1: Delete slf4j-simple/src/main/java/org/slf4j/LoggerFactory.java
Step #2: Create
slf4j-simple/src/main/resources/META-INF/services/org.slf4j.ILoggerFactory
Step #3: Add a line the the above resource file which reads "
org.slf4j.impl.SimpleLoggerFactory"

This can be repeated for each SLF4J provider (implementation of the API).

Benefits:

* slf4j-api is now complete - this is *all* people need to build against and
depend on
* This exact same approach can be used to resolve the similar issue it looks
like the MarkerFactory faces.
* This preserves the existing behavior of deploy-what-you-want. Its still 0
config to select an implementation.


Drawbacks:

* Depends on sun.misc -- (resolvable)
  Although this is an internal sun class, they actually do a good job of
maintaining it, and it exists in SE4, SE5 & SE6.
  It's understandable to be nervous about depending on it. Maybe some
cleanroom implementations don't support it (blackdown?)

  We could write a simpler version of the Service class which does the same
thing, its really not that much code and I could provide this if you want.


I would personally like to see such an enhancement to SLF4J, I don't know
about others. Any thoughts or comments?

-- 

- Eric
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://qos.ch/pipermail/slf4j-dev/attachments/20070209/a75981a9/attachment.htm>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: LoggerFactory.java
Type: application/octet-stream
Size: 1561 bytes
Desc: not available
URL: <http://qos.ch/pipermail/slf4j-dev/attachments/20070209/a75981a9/attachment.obj>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: slf4j-1.2-patch.tgz
Type: application/x-gzip
Size: 988 bytes
Desc: not available
URL: <http://qos.ch/pipermail/slf4j-dev/attachments/20070209/a75981a9/attachment.bin>


More information about the slf4j-dev mailing list