nahab
nahab

Reputation: 1354

How slf4j replace org.slf4j.impl.StaticLoggerBinder placed in sfl4j-api to real StaticLoggerBinder

There are exist classes org.slf4j.impl.StaticLoggerBinder, org.slf4j.impl.StaticMarkerBinder, org.slf4j.impl.StaticMDCBinder in slf4j api. But each binding to concrete logger should contain the same classes.

For example:

http://grepcode.com/file/repo1.maven.org/maven2/org.slf4j/slf4j-api/1.6.1/org/slf4j/impl/StaticLoggerBinder.java?av=f

and its log4j implementation: http://grepcode.com/file/repo1.maven.org/maven2/org.slf4j/slf4j-log4j12/1.6.1/org/slf4j/impl/StaticLoggerBinder.java?av=f

How java classloaders substitute it? Shouldn't here be an exception?

Upvotes: 3

Views: 4265

Answers (2)

kodstark
kodstark

Reputation: 490

hm, good question as slf4j is suppose to not use runtime loading classes. It looks that uses classloader checks at startup in below method:

private static Set findPossibleStaticLoggerBinderPathSet() {
    // use Set instead of list in order to deal with  bug #138
    // LinkedHashSet appropriate here because it preserves insertion order during iteration
    Set staticLoggerBinderPathSet = new LinkedHashSet();
    try {
      ClassLoader loggerFactoryClassLoader = LoggerFactory.class
              .getClassLoader();
      Enumeration paths;
      if (loggerFactoryClassLoader == null) {
        paths = ClassLoader.getSystemResources(STATIC_LOGGER_BINDER_PATH);
      } else {
        paths = loggerFactoryClassLoader
                .getResources(STATIC_LOGGER_BINDER_PATH);
      }
      while (paths.hasMoreElements()) {
        URL path = (URL) paths.nextElement();
        staticLoggerBinderPathSet.add(path);
      }
    } catch (IOException ioe) {
      Util.report("Error getting resources from path", ioe);
    }
    return staticLoggerBinderPathSet;
}

@Dev is right - org.slf4j.impl.StaticLoggerBinder is included only in slf4j-api sources and not binaries - Including only in sources is simple trick for compile reasons. It is understandable that findPossibleStaticLoggerBinderPathSet is used only for logging purposes.

Upvotes: 1

Dev
Dev

Reputation: 12196

If you extract the actual slf4j-api jar you'll notice that org.slf4j.impl.StaticLoggerBinder is not actually included in the jar. SFL4J api is compiled against the class at build but it isn't actually included in the artifact. If you look at the source for org.slf4j.impl.StaticLoggerBinder in the api module it has no implementation, all the public instance methods throw UnsupportedOperationException. This is ok, again because that class is excluded from the slf4j-api jar.

The classloader behaves normally and select the first version of org.slf4j.impl.StaticLoggerBinder that is found when the class needs to be loaded. This will generally be from the first slf4j implementation jar that it listed on the classpath.

Note: The findPossibleStaticLoggerBinderPathSet() is only used to warn that there are multiple bindings present on the classpath. It does not actually load any bindings.

Upvotes: 5

Related Questions