slf4j/logback 随笔 初始化

0. 许久没听Tank了

日志框架统一解决方案(mvn.exclude)
slf4j门面设计整合spring的日志方案


  • qos.slf4j、apache.commons-logging 均是 统一日志接口
  • 基本上都是 qos.slf4j + apache.log4j2 / qos.logback
  • slf4j 整合 spring的日志方案的图还有意思,盗过来看看
    slf4j/logback 随笔 初始化_第1张图片

原来适配spring这一块的逻辑走的是SPI
slf4j/logback 随笔 初始化_第2张图片

1. 静态绑定 与 SPI

  • 通过 lombok 引入的 slf4j 版本有点旧,不小心就遇到静态绑定的 “古老的”方案

  • slf4j 1.8+ 改成了 JDK6 所推崇的 SPI

  • 静态绑定,简单说就是在第三方实现库(例如 logback)中放接口库(org.slf4j…XxxBinder)的代码来引入自己的实现,放个图就懂了

slf4j/logback 随笔 初始化_第3张图片

  • slf4j.LoggerFactory -> 第三方日志框架.LoggerFactory -> Logger

2. slf4j 静态绑定 logback

package org.slf4j;

public final class LoggerFactory {

	// 注意:slf4j.LoggerFactory 并没有实现 ILoggerFactory 接口
	// slf4j.LoggerFactory 是用来 获取 第三方厂商的ILoggerFactory 的
    public static Logger getLogger(Class<?> clazz) {
		// step into ...
        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 " + LOGGER_NAME_MISMATCH_URL + " for an explanation");
            }
        }
        return logger;
    }

	public static Logger getLogger(String name) {
		// step into ...
		// 先获取 LoggerFactory
        ILoggerFactory iLoggerFactory = getILoggerFactory();
		// step into ...
		// 再获取 Logger
        return iLoggerFactory.getLogger(name);
    }

    public static ILoggerFactory getILoggerFactory() {
        if (INITIALIZATION_STATE == UNINITIALIZED) {
            synchronized (LoggerFactory.class) {
                if (INITIALIZATION_STATE == UNINITIALIZED) {
                    INITIALIZATION_STATE = ONGOING_INITIALIZATION;
                    performInitialization();
                }
            }
        }
        switch (INITIALIZATION_STATE) {
		// 初始化成功后
        case SUCCESSFUL_INITIALIZATION:
			// step into ...
			// 第三方厂商会有一个 jar/org/slf4j/impl/StaticLoggerBinder 这样子的类路径
			// slf4j 1.7 版本的做法:由第三方类通过 实现绑定类 来做到静态绑定
            return StaticLoggerBinder.getSingleton().getLoggerFactory();
        case NOP_FALLBACK_INITIALIZATION:
            return NOP_FALLBACK_FACTORY;
        case FAILED_INITIALIZATION:
            throw new IllegalStateException(UNSUCCESSFUL_INIT_MSG);
        case ONGOING_INITIALIZATION:
            // support re-entrant behavior.
            // See also http://jira.qos.ch/browse/SLF4J-97
            return SUBST_FACTORY;
        }
        throw new IllegalStateException("Unreachable code");
    }
}

-------

// 此时是在 第三方的jar目录中(这里使用 logback-classic 演示)
package org.slf4j.impl;

public class StaticLoggerBinder implements LoggerFactoryBinder {

	// step into ...
	// 3. logback 初始化 loggerFactory、logger
	// public class LoggerContext extends ContextBase implements ILoggerFactory, LifeCycle
	// 可见: LoggerContext is a ILoggerFactory
	// 看看 默认的LoggerFactory 的构造
    private LoggerContext defaultLoggerContext = new LoggerContext();
    private StaticLoggerBinder() {
        defaultLoggerContext.setName(CoreConstants.DEFAULT_CONTEXT_NAME);
    }

    private static StaticLoggerBinder SINGLETON = new StaticLoggerBinder();
    public static StaticLoggerBinder getSingleton() {
        return SINGLETON;
    }

	// 这意味着 LoggerContext(LoggerFactory) 是1个单例
    private final ContextSelectorStaticBinder contextSelectorBinder = ContextSelectorStaticBinder.getSingleton();
    static {
		// step into ...
        SINGLETON.init();
    }
    void init() {
        try {
            try {
           		// 4. 读取日志配置文件
				// step into autoConfig() ...
                new ContextInitializer(defaultLoggerContext).autoConfig();
            } catch (JoranException je) {
                Util.report("Failed to auto configure default logger context", je);
            }
            // logback-292
            if (!StatusUtil.contextHasStatusListener(defaultLoggerContext)) {
                StatusPrinter.printInCaseOfErrorsOrWarnings(defaultLoggerContext);
            }
			
            contextSelectorBinder.init(defaultLoggerContext, KEY);
            initialized = true;
        } catch (Exception t) { // see LOGBACK-1159
            Util.report("Failed to instantiate [" + LoggerContext.class.getName() + "]", t);
        }
    }

    public ILoggerFactory getLoggerFactory() {
        if (!initialized) {
			// 如果没有完成初始化就需要获取1个LoggerFactory,则返回默认的LoggerFactory
            return defaultLoggerContext;
        }

        if (contextSelectorBinder.getContextSelector() == null) {
            throw new IllegalStateException("contextSelector cannot be null. See also " + NULL_CS_URL);
        }
        return contextSelectorBinder.getContextSelector().getLoggerContext();
    }

}

3. logback 初始化 loggerFactory、logger

package ch.qos.logback.classic;

public class LoggerContext extends ContextBase implements ILoggerFactory, LifeCycle {

	// 单例的根节点
    final Logger root;

    private Map<String, Logger> loggerCache;

	// LoggerFactory 构造
    public LoggerContext() {
        super();
        this.loggerCache = new ConcurrentHashMap<String, Logger>();
        this.loggerContextRemoteView = new LoggerContextVO(this);

		// 初始化Logger根节点
        this.root = new Logger(Logger.ROOT_LOGGER_NAME, null, this);
        this.root.setLevel(Level.DEBUG);
		
		// 缓存Logger根节点
        loggerCache.put(Logger.ROOT_LOGGER_NAME, root);
        initEvaluatorMap();
        size = 1;
        this.frameworkPackages = new ArrayList<String>();
    }

    public final Logger getLogger(final Class<?> clazz) {
        return getLogger(clazz.getName());
    }

    @Override
    public final Logger getLogger(final String name) {

        if (name == null) {
            throw new IllegalArgumentException("name argument cannot be null");
        }

		// 直接返回 Logger 根节点
        // if we are asking for the root logger, then let us return it without
        // wasting time
        if (Logger.ROOT_LOGGER_NAME.equalsIgnoreCase(name)) {
            return root;
        }

        int i = 0;
        Logger logger = root;

        // check if the desired logger exists, if it does, return it
        // without further ado.
        Logger childLogger = (Logger) loggerCache.get(name);
        // if we have the child, then let us return it without wasting time
        if (childLogger != null) {
            return childLogger;
        }

        // if the desired logger does not exist, them create all the loggers
        // in between as well (if they don't already exist)
        String childName;
        while (true) {
			// 循环获取(并缓存)下一层级的Logger,直至当前类的Logger
			// 层级按照包的分隔符(.)来分隔
			// 当找到当前类层级的logger即返回
            int h = LoggerNameUtil.getSeparatorIndexOf(name, i);
            if (h == -1) {
                childName = name;
            } else {
                childName = name.substring(0, h);
            }
            // move i left of the last point
            i = h + 1;
            synchronized (logger) {
                childLogger = logger.getChildByName(childName);
                if (childLogger == null) {
                    childLogger = logger.createChildByName(childName);
                    loggerCache.put(childName, childLogger);
                    incSize();
                }
            }
            logger = childLogger;
            if (h == -1) {
                return childLogger;
            }
        }
    }
	
	final void noAppenderDefinedWarning(final Logger logger) {
        if (noAppenderWarning++ == 0) {
			// TODO 有说法!!!
            getStatusManager().add(new WarnStatus("No appenders present in context [" + getName() + "] for logger [" + logger.getName() + "].", logger));
        }
    }

}

4. 读取日志配置文件

	// ch.qos.logback.classic.util.ContextInitializer#autoConfig
    public void autoConfig() throws JoranException {
        StatusListenerConfigHelper.installIfAsked(loggerContext);
		// 从默认的文件中获取有效的路径
		// step into ...
        URL url = findURLOfDefaultConfigurationFile(true);
        if (url != null) {
            configureByResource(url);
        } else {
            Configurator c = EnvUtil.loadFromServiceLoader(Configurator.class);
            if (c != null) {
                try {
                    c.setContext(loggerContext);
                    c.configure(loggerContext);
                } catch (Exception e) {
                    throw new LogbackException(String.format("Failed to initialize Configurator: %s using ServiceLoader", c != null ? c.getClass()
                                    .getCanonicalName() : "null"), e);
                }
            } else {
                BasicConfigurator basicConfigurator = new BasicConfigurator();
                basicConfigurator.setContext(loggerContext);
                basicConfigurator.configure(loggerContext);
            }
        }
    }

	// ch.qos.logback.classic.util.ContextInitializer#findURLOfDefaultConfigurationFile
    public URL findURLOfDefaultConfigurationFile(boolean updateStatus) {
        ClassLoader myClassLoader = Loader.getClassLoaderOfObject(this);
        URL url = findConfigFileURLFromSystemProperties(myClassLoader, updateStatus);
        if (url != null) {
            return url;
        }

		// final public static String TEST_AUTOCONFIG_FILE = "logback-test.xml";
        url = getResource(TEST_AUTOCONFIG_FILE, myClassLoader, updateStatus);
        if (url != null) {
            return url;
        }

		// final public static String GROOVY_AUTOCONFIG_FILE = "logback.groovy";
        url = getResource(GROOVY_AUTOCONFIG_FILE, myClassLoader, updateStatus);
        if (url != null) {
            return url;
        }

		// final public static String AUTOCONFIG_FILE = "logback.xml";
        return getResource(AUTOCONFIG_FILE, myClassLoader, updateStatus);
    }

你可能感兴趣的:(Java,logback,log4j,java)