“SLF4J 日志绑定原理”的版本间的差异

来自姬鸿昌的知识库
跳到导航 跳到搜索
 
第167行: 第167行:
 
}
 
}
 
</syntaxhighlight>
 
</syntaxhighlight>
 +
 +
 +
 +
 +
 +
=== SLF4J 原理解析 ===
 +
 +
# SLF4J 通过 LoggerFactory 加载日志具体的实现对象
 +
# LoggerFactory 在初始化的过程中,会通过 performInitialization() 方法绑定具体的日志实现
 +
# 在绑定具体的日志实现的时候,通过类加载器,加载 org/slf4j/impl/StaticLoggerBinder.class
 +
# 所以,只要是一个日志实现框架,在 org.slf4j.impl 包中提供一个自己的 StaticLoggerBinder 类,在其中提供具体日志实现的 LoggerFactory 就可以被 SLF4J 加载

2023年2月27日 (一) 02:39的最新版本

https://www.bilibili.com/video/BV1iJ411H74S?p=24

源码跟踪

SLF4JTest.java

……
public class SLF4JTest {

    public static final Logger LOGGER = LoggerFactory.getLogger(SLF4JTest.class);
    ……
}

LoggerFactory.java

……
public final class LoggerFactory {

    ……
    public static Logger getLogger(String name) {
        ILoggerFactory iLoggerFactory = getILoggerFactory();
        return iLoggerFactory.getLogger(name);
    }
    public static Logger getLogger(Class<?> clazz) {
        Logger logger = getLogger(clazz.getName());
        if (DETECT_LOGGER_NAME_MISMATCH) {
            Class<?> autoComputedCallingClass = Util.getCallingClass();
            if (autoComputedCallingClass != null && nonMatchingClasses(clazz, autoComputedCallingClass)) {
                Util.report(String.format("Detected logger name mismatch. Given name: \"%s\"; computed name: \"%s\".", logger.getName(), autoComputedCallingClass.getName()));
                Util.report("See http://www.slf4j.org/codes.html#loggerNameMismatch for an explanation");
            }
        }

        return logger;
    }
    ……
    public static ILoggerFactory getILoggerFactory() {
        if (INITIALIZATION_STATE == 0) {
            Class var0 = LoggerFactory.class;
            synchronized(LoggerFactory.class) {
                if (INITIALIZATION_STATE == 0) {
                    INITIALIZATION_STATE = 1;
                    performInitialization();
                }
            }
        }

        switch (INITIALIZATION_STATE) {
            case 1:
                return SUBST_FACTORY;
            case 2:
                throw new IllegalStateException("org.slf4j.LoggerFactory in failed state. Original exception was thrown EARLIER. See also http://www.slf4j.org/codes.html#unsuccessfulInit");
            case 3:
                return StaticLoggerBinder.getSingleton().getLoggerFactory();
            case 4:
                return NOP_FALLBACK_FACTORY;
            default:
                throw new IllegalStateException("Unreachable code");
        }
    }
    ……
    private static final void performInitialization() {
        bind();
        if (INITIALIZATION_STATE == 3) {
            versionSanityCheck();
        }

    }
    ……
    private static final void bind() {
        String msg;
        try {
            Set<URL> staticLoggerBinderPathSet = null;
            if (!isAndroid()) {
                staticLoggerBinderPathSet = findPossibleStaticLoggerBinderPathSet();
                reportMultipleBindingAmbiguity(staticLoggerBinderPathSet);
            }

            StaticLoggerBinder.getSingleton();
            INITIALIZATION_STATE = 3;
            reportActualBinding(staticLoggerBinderPathSet);
            fixSubstituteLoggers();
            replayEvents();
            SUBST_FACTORY.clear();
        } catch (NoClassDefFoundError var2) {
            msg = var2.getMessage();
            if (!messageContainsOrgSlf4jImplStaticLoggerBinder(msg)) {
                failedBinding(var2);
                throw var2;
            }

            INITIALIZATION_STATE = 4;
            Util.report("Failed to load class \"org.slf4j.impl.StaticLoggerBinder\".");
            Util.report("Defaulting to no-operation (NOP) logger implementation");
            Util.report("See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.");
        } catch (NoSuchMethodError var3) {
            msg = var3.getMessage();
            if (msg != null && msg.contains("org.slf4j.impl.StaticLoggerBinder.getSingleton()")) {
                INITIALIZATION_STATE = 2;
                Util.report("slf4j-api 1.6.x (or later) is incompatible with this binding.");
                Util.report("Your binding is version 1.5.5 or earlier.");
                Util.report("Upgrade your binding to version 1.6.x.");
            }

            throw var3;
        } catch (Exception var4) {
            failedBinding(var4);
            throw new IllegalStateException("Unexpected initialization failure", var4);
        }

    }
    ……
}

调用顺序:LoggerFactory.getLogger(Class<?> clazz)

-> LoggerFactory.getLogger(String name)

-> LoggerFactory.getILoggerFactory()

-> LoggerFactory.performInitialization()

-> LoggerFactory.bind()

-> LoggerFactory.findPossibleStaticLoggerBinderPathSet()

    private static String STATIC_LOGGER_BINDER_PATH = "org/slf4j/impl/StaticLoggerBinder.class";

    static Set<URL> findPossibleStaticLoggerBinderPathSet() {
        Set<URL> 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 var4) {
            Util.report("Error getting resources from path", var4);
        }

        return staticLoggerBinderPathSet;
    }

org.slf4j.impl.StaticLoggerBinder.java

package org.slf4j.impl;
……
public class StaticLoggerBinder implements LoggerFactoryBinder {
……
    private final ILoggerFactory loggerFactory = new JDK14LoggerFactory();
……
}

JDK14LoggerFactory.java

package org.slf4j.impl;
……
public class JDK14LoggerFactory implements ILoggerFactory {
……
    public JDK14LoggerFactory() {
        java.util.logging.Logger.getLogger("");
    }
……
}



SLF4J 原理解析

  1. SLF4J 通过 LoggerFactory 加载日志具体的实现对象
  2. LoggerFactory 在初始化的过程中,会通过 performInitialization() 方法绑定具体的日志实现
  3. 在绑定具体的日志实现的时候,通过类加载器,加载 org/slf4j/impl/StaticLoggerBinder.class
  4. 所以,只要是一个日志实现框架,在 org.slf4j.impl 包中提供一个自己的 StaticLoggerBinder 类,在其中提供具体日志实现的 LoggerFactory 就可以被 SLF4J 加载