SLF4J source code analysis

Ask questions

Before reading the source code, first of all, ask a few questions

  • How SLF4J integrates different log frameworks
  • Why there is only one binding of log frame in Class Path

In your code, in addition to slf4j-api-1.8.0-beta2.jar, you simply drop one and only one binding of your choice on the appropriate class path location. Do not place more than one binding on your class path

Source version

  • org.slf4j:slf4j-api:1.7.25
  • org.apache.logging.log4j:log4j-slf4j-impl:2.11.1
  • ch.qos.logback:logback-classic:1.2.3
  • org.slf4j:slf4j-jcl:1.7.25

Source code analysis

Look at the source code with the above two questions

  1. Find the binding of the log frame by calling findPossibleStaticLoggerBinderPathSet() method in the bind() method
// org/slf4j/slf4j-api/1.7.25/slf4j-api-1.7.25.jar!/org/slf4j/LoggerFactory.class
private static final void bind() {
    // ...
    if (!isAndroid()) {
        // Find the binding of the log framework
        staticLoggerBinderPathSet = findPossibleStaticLoggerBinderPathSet();
        reportMultipleBindingAmbiguity(staticLoggerBinderPathSet);
    }
    // ...
}
  1. In the findPossibleStaticLoggerBinderPathSet() method, use ClassLoader or loggerFactoryClassLoader to obtain the Resources named "static? Logger? Binder? Path"
    The value of "static? Logger? Binder? Path" has been defined at the beginning of the file
private static String STATIC_LOGGER_BINDER_PATH = "org/slf4j/impl/StaticLoggerBinder.class";
// org/slf4j/slf4j-api/1.7.25/slf4j-api-1.7.25.jar!/org/slf4j/LoggerFactory.class
    static Set<URL> findPossibleStaticLoggerBinderPathSet() {
        LinkedHashSet staticLoggerBinderPathSet = new LinkedHashSet();

        try {
            ClassLoader loggerFactoryClassLoader = LoggerFactory.class.getClassLoader();
            Enumeration paths;
            // Obtain Resources through ClassLoader or loggerFactoryClassLoader
            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 var4) {
            Util.report("Error getting resources from path", var4);
        }

        return staticLoggerBinderPathSet;
    }
  1. Take the example of integrating Log4j here. "org/slf4j/impl/StaticLoggerBinder.class" is in the log4j-slf4j-impl Library of log4j. This class is responsible for initializing Log4j related classes. Staticloggerbinder implements the LoggerFactoryBinder interface.
// org/apache/logging/log4j/log4j-slf4j-impl/2.11.1/log4j-slf4j-impl-2.11.1.jar!/org/slf4j/impl/StaticLoggerBinder.class

// The package name here is defined as org.slf4j.impl, so that the Class Loader in step 2 above can be loaded into this class
package org.slf4j.impl;

public final class StaticLoggerBinder implements LoggerFactoryBinder {
// log4j init...
}
  1. Other logging frameworks also define the StaticLoggerBinder class and implement the LoggerFactoryBinder interface.
    For example: logback, jcl
// ch/qos/logback/logback-classic/1.2.3/logback-classic-1.2.3.jar!/org/slf4j/impl/StaticLoggerBinder.class

// The package name here is defined as org.slf4j.impl, so that the Class Loader in step 2 above can be loaded into this class
package org.slf4j.impl;

public class StaticLoggerBinder implements LoggerFactoryBinder {
// logback init...
}
// org/slf4j/slf4j-jcl/1.7.25/slf4j-jcl-1.7.25.jar!/org/slf4j/impl/StaticLoggerBinder.class

// The package name here is defined as org.slf4j.impl, so that the Class Loader in step 2 above can be loaded into this class
package org.slf4j.impl;

public class StaticLoggerBinder implements LoggerFactoryBinder {
// jcl init...
}

Answers to questions

  • How SLF4J integrates different log frameworks
    SLF4J loads and initializes the corresponding log framework by loading the org/slf4j/impl/StaticLoggerBinder.class of each underlying log framework bridging library.
  • Why there is only one binding of log frame in Class Path
    In LoggerFactory in slf4j API, after returning the static loggerbinderpathset in step 2 above, it will immediately call
    Report multiple binding ambiguities (static loggerbinderpathset) method to detect whether there are multiple bindings. If there are multiple bindings, the error message "Class path contains multiple SLF4J bindings" will be output
    private static void reportMultipleBindingAmbiguity(Set<URL> binderPathSet) {
        if (isAmbiguousStaticLoggerBinderPathSet(binderPathSet)) {
            Util.report("Class path contains multiple SLF4J bindings.");
            Iterator i$ = binderPathSet.iterator();

            while(i$.hasNext()) {
                URL path = (URL)i$.next();
                Util.report("Found binding in [" + path + "]");
            }

            Util.report("See http://www.slf4j.org/codes.html#multiple_bindings for an explanation.");
        }

    }

last

SLF4J is used to record logs in the code. You can switch the underlying log framework without modifying the code. You only need to update the dependency and log configuration files.

Tags: Programming log4j Apache

Posted on Mon, 11 May 2020 02:29:08 -0700 by Jack McSlay