logback的配置文件加载顺序

logback的配置文件加载顺序

logback的配置文件加载过程还是很简单的,这里做一下简单记录

1、maven

logback-classic已经包含了logback-coreslf4j的依赖,不需要额外引入了

        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.2.10</version>
        </dependency>

2、入口

平时我们使用 logback入口都是 LoggerFactory.getLogger() 这个 api,我们debug就从这里进入

    public static Logger getLogger(String name) {
        ILoggerFactory iLoggerFactory = getILoggerFactory();
        return iLoggerFactory.getLogger(name);
    }

进入 getILoggerFactory()方法
这个方法里面使用了一堆常量,这里简单介绍

常量 含义
UNINITIALIZED 还未初始化,需要初始化日志环境
SUCCESSFUL_INITIALIZATION 成功初始化,可以执行获取日志对象
NOP_FALLBACK_INITIALIZATION 已经初始化了,但是没有只有门面,没有实现,所以会返回 NOPLoggerFactory ,这个工厂中创建的日志对象什么都不处理,也就是打印不出来日志
FAILED_INITIALIZATION 初始化失败,无法使用,会报错
ONGOING_INITIALIZATION 正在初始化,表明多线程环境下,第一个线程使用已经开始初始化了,但是还没有初始化完成,第二个线程又进来这时候就会返回一个SubstituteLoggerFactory,临时使用这个日志工厂创建日志对象
INITIALIZATION_STATE 当前的slf4j的状态,默认为UNINITIALIZED
    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:
            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");
    }

详细的流程这里不说了,因为我们要看的是日志的配置文件加载过程,所以此时第一次进入肯定是未初始化UNINITIALIZED,只需要看performInitialization方法即可,我们进入

    private final static void performInitialization() {
        bind();
        if (INITIALIZATION_STATE == SUCCESSFUL_INITIALIZATION) {
            versionSanityCheck();
        }
    }

绑定完成后,会将当前状态修改为初始化完成,我们进入 bind方法

    private final static void bind() {
        try {
            Set<URL> staticLoggerBinderPathSet = null;
            if (!isAndroid()) {
                staticLoggerBinderPathSet = findPossibleStaticLoggerBinderPathSet();
                reportMultipleBindingAmbiguity(staticLoggerBinderPathSet);
            }
            // 日志绑定对象
            StaticLoggerBinder.getSingleton();
            INITIALIZATION_STATE = SUCCESSFUL_INITIALIZATION;
            reportActualBinding(staticLoggerBinderPathSet);
        } catch (NoClassDefFoundError ncde) {
             //省略异常处理流程
        }
    }

StaticLoggerBinder是个单例,它初始化的过程,不仅决定使用那个实现,还决定配置文件的加载,我们进入getSingleton方法

    private static StaticLoggerBinder SINGLETON = new StaticLoggerBinder();

    static {
        SINGLETON.init();
    }

    public static StaticLoggerBinder getSingleton() {
        return SINGLETON;
    }

可以看到StaticLoggerBinder在实例化过程中,会调用init方法,我们进入

    void init() {
        try {
            try {
                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);
        }
    }

这里面紧急执行了 autoConfig方法,我们进入

    public void autoConfig() throws JoranException {
        StatusListenerConfigHelper.installIfAsked(loggerContext);
        //获取默认的配置文件url,源码可见2.1
        URL url = findURLOfDefaultConfigurationFile(true);
        // 如果获取到则进行配置
        if (url != null) {
            configureByResource(url);
        } 
        // 没有获取到默认的配置文件url,则继续判断
        else {
            // 通过jdk的spi机制获取配置文件对象,源码可见2.2
            Configurator c = EnvUtil.loadFromServiceLoader(Configurator.class);
            // 获取到配置文件对象,则对loggerContext进行配置
            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);
                }
            } 
            // 未获取到配置文件对象,则使用BasicConfigurator进行配置,
            else {
                BasicConfigurator basicConfigurator = new BasicConfigurator();
                basicConfigurator.setContext(loggerContext);
                basicConfigurator.configure(loggerContext);
            }
        }
    }

2.1、获取默认的配置文件url

    public URL findURLOfDefaultConfigurationFile(boolean updateStatus) {
        // 获取配置文件
        ClassLoader myClassLoader = Loader.getClassLoaderOfObject(this);
        // 获取系统变量中指定的名为,logback.configurationFile,的配置文件
        URL url = findConfigFileURLFromSystemProperties(myClassLoader, updateStatus);
        if (url != null) {
            return url;
        }
        // 获取名为logback-test.xml的配置文件
        url = getResource(TEST_AUTOCONFIG_FILE, myClassLoader, updateStatus);
        if (url != null) {
            return url;
        }
        // 获取名为logback.xml的配置文件
        return getResource(AUTOCONFIG_FILE, myClassLoader, updateStatus);
    }

2.2、通过java spi 获取配置对象

    public static <T> T loadFromServiceLoader(Class<T> c) {
        ServiceLoader<T> loader = ServiceLoader.load(c, getServiceLoaderClassLoader());
        Iterator<T> it = loader.iterator();
        if (it.hasNext())
            return it.next();
        return null;
    }

3、总结

在第一次获取Logger对象时,会加载配置文件,顺序是

  1. 系统变量中的名为 logback.configurationFile的配置文件
  2. 类路径下的名为 logback-test.xml的配置文件
  3. 类路径下的名为 logback-test.xml的配置文件
  4. java spi 机制实现了ch.qos.logback.classic.spi.Configurator接口的类
  5. 默认的配置类BasicConfigurator
hmoban主题是根据ripro二开的主题,极致后台体验,无插件,集成会员系统
自学咖网 » logback的配置文件加载顺序