slf4j+logback源码加载流程解析

slf4j绑定logback源码解析

java 复制代码
Logger log = LoggerFactory.getLogger(LogbackDemo.class);

如上述代码所示,在项目中通常会这样创建一个Logger对象去打印日志。

然后点进去,会走到LoggerFactory的getILoggerFactory()方法,如下代码所示。

java 复制代码
	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");
    }

performInitialization()方法表示执行初始化,点进去会调用到LoggerFactory的bind()方法,如下代码所示。

java 复制代码
			Set<URL> staticLoggerBinderPathSet = null;
            if (!isAndroid()) {
                // 查找org/slf4j/impl/StaticLoggerBinder.class这个类的路径
                staticLoggerBinderPathSet = findPossibleStaticLoggerBinderPathSet();
                // 如果有多个则打印Class path contains multiple SLF4J bindings
                reportMultipleBindingAmbiguity(staticLoggerBinderPathSet);
            }
            // StaticLoggerBinder类是各个日志框架提供的,比如logback,如下图所示
            StaticLoggerBinder.getSingleton();
            INITIALIZATION_STATE = SUCCESSFUL_INITIALIZATION;
            reportActualBinding(staticLoggerBinderPathSet);
            fixSubstituteLoggers();
            replayEvents();
            // release all resources in SUBST_FACTORY
            SUBST_FACTORY.clear();

StaticLoggerBinder类加载时会执行初始化,如下代码所示。

java 复制代码
	static {
        SINGLETON.init();
    }
    void init() {
        try {
            try {
            	// 这里会完成logback的自动配置
                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);
        }
    }

logback源码解析

ContextInitializer.autoConfig()方法源码如下:

java 复制代码
    public void autoConfig() throws JoranException {
        StatusListenerConfigHelper.installIfAsked(loggerContext);
        // 查找配置文件,优先级:系统属性logback.configurationFile > logback-test.xml > logback.xml
        URL url = findURLOfDefaultConfigurationFile(true);
        if (url != null) {
            configureByResource(url);
        } else {
        	// 使用java的spi机制,查找Configurator的实现,如果有,则自动配置logback
            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来自动配置logback
                BasicConfigurator basicConfigurator = new BasicConfigurator();
                basicConfigurator.setContext(loggerContext);
                basicConfigurator.configure(loggerContext);
            }
        }
    }

spring boot初始化logback流程

Spring Boot启动的时候,由org.springframework.boot.context.logging.LoggingApplicationListener根据情况初始化并使用。

在spring 初始化启动的过程中,会根据生命周期的不同阶段,发出对应的动作。这就是Spring ApplicationListener,设计基于观察者模式,而其中LoggingApplicationListener类便是负责logging日志框架的初始化操作。

LoggingApplicationListener被配置在spring-boot-x.x.x.jar的spring.factories文件中,spring启动的时候会去读取这个文件。

LoggingApplicationListener.onApplicationEvent()方法源码如下。

java 复制代码
	public void onApplicationEvent(ApplicationEvent event) {
		if (event instanceof ApplicationStartingEvent) {
			onApplicationStartingEvent((ApplicationStartingEvent) event);
		}
		else if (event instanceof ApplicationEnvironmentPreparedEvent) {
			onApplicationEnvironmentPreparedEvent((ApplicationEnvironmentPreparedEvent) event);
		}
		else if (event instanceof ApplicationPreparedEvent) {
			onApplicationPreparedEvent((ApplicationPreparedEvent) event);
		}
		else if (event instanceof ContextClosedEvent
				&& ((ContextClosedEvent) event).getApplicationContext().getParent() == null) {
			onContextClosedEvent();
		}
		else if (event instanceof ApplicationFailedEvent) {
			onApplicationFailedEvent();
		}
	}

第一个事件就是ApplicationStartingEvent了,点进去到LoggingSystem.get()方法。

因此,最终返回出去的是LogbackLoggingSystem对象,然后执行loggingSystem.beforeInitialize()方法,beforeInitialize会使用到logback的StaticLoggerBinder类,因此会读取logback.xml完成logback的初始化。

第二个事件是ApplicationEnvironmentPreparedEvent,会执行日志系统的初始化。

java 复制代码
	protected void initialize(ConfigurableEnvironment environment, ClassLoader classLoader) {
		new LoggingSystemProperties(environment).apply();
		this.logFile = LogFile.get(environment);
		if (this.logFile != null) {
			this.logFile.applyToSystemProperties();
		}
		this.loggerGroups = new LoggerGroups(DEFAULT_GROUP_LOGGERS);
		initializeEarlyLoggingLevel(environment);
		initializeSystem(environment, this.loggingSystem, this.logFile);
		initializeFinalLoggingLevels(environment, this.loggingSystem);
		registerShutdownHookIfNecessary(environment, this.loggingSystem);
	}
	// 主要看initializeSystem方法
	private void initializeSystem(ConfigurableEnvironment environment, LoggingSystem system, LogFile logFile) {
		LoggingInitializationContext initializationContext = new LoggingInitializationContext(environment);
		// 如果application.yml里指定了logging.config,则使用指定的文件初始化,否则按默认处理
		String logConfig = environment.getProperty(CONFIG_PROPERTY);
		if (ignoreLogConfig(logConfig)) {
			system.initialize(initializationContext, null, logFile);
		}
		else {
			try {
				ResourceUtils.getURL(logConfig).openStream().close();
				system.initialize(initializationContext, logConfig, logFile);
			}
			catch (Exception ex) {
				// NOTE: We can't use the logger here to report the problem
				System.err.println("Logging system failed to initialize using configuration from '" + logConfig + "'");
				ex.printStackTrace(System.err);
				throw new IllegalStateException(ex);
			}
		}
	}
	// 下面会走到LogbackLoggingSystem.initialize()方法
	public void initialize(LoggingInitializationContext initializationContext, String configLocation, LogFile logFile) {
		LoggerContext loggerContext = getLoggerContext();
		if (isAlreadyInitialized(loggerContext)) {
			return;
		}
		super.initialize(initializationContext, configLocation, logFile);
		loggerContext.getTurboFilterList().remove(FILTER);
		markAsInitialized(loggerContext);
		if (StringUtils.hasText(System.getProperty(CONFIGURATION_FILE_PROPERTY))) {
			getLogger(LogbackLoggingSystem.class.getName()).warn("Ignoring '" + CONFIGURATION_FILE_PROPERTY
					+ "' system property. Please use 'logging.config' instead.");
		}
	}
	// 再到AbstractLoggingSystem.initialize()方法
	public void initialize(LoggingInitializationContext initializationContext, String configLocation, LogFile logFile) {
		if (StringUtils.hasLength(configLocation)) {
			initializeWithSpecificConfig(initializationContext, configLocation, logFile);
			return;
		}
		initializeWithConventions(initializationContext, logFile);
	}
	// 再到initializeWithConventions方法
	private void initializeWithConventions(LoggingInitializationContext initializationContext, LogFile logFile) {
		// 查找logback自己的配置文件logback-test.xml、logback.xml
		String config = getSelfInitializationConfig();
		if (config != null && logFile == null) {
			// logback在前面第一次使用StaticLoggerBinder时已经完成了初始化,这里会重新初始化一次
			reinitialize(initializationContext);
			return;
		}
		if (config == null) {
			// 获取logback-spring.xml
			config = getSpringInitializationConfig();
		}
		if (config != null) {
			// 加载配置logback-spring.xml
			loadConfiguration(initializationContext, config, logFile);
			return;
		}
		// 加载默认配置
		loadDefaults(initializationContext, logFile);
	}
	// reinitialize方法会走到LogbackLoggingSystem.configureByResourceUrl()方法,这里使用了SpringBootJoranConfigurator
	private void configureByResourceUrl(LoggingInitializationContext initializationContext, LoggerContext loggerContext,
			URL url) throws JoranException {
		if (url.toString().endsWith("xml")) {
			JoranConfigurator configurator = new SpringBootJoranConfigurator(initializationContext);
			configurator.setContext(loggerContext);
			configurator.doConfigure(url);
		}
		else {
			// 否则使用logback的ContextInitializer进行初始化,这就不支持springProperty标签
			new ContextInitializer(loggerContext).configureByResource(url);
		}
	}
	// SpringBootJoranConfigurator添加了额外的规则,如springProperty标签
	class SpringBootJoranConfigurator extends JoranConfigurator {
		@Override
		public void addInstanceRules(RuleStore rs) {
			super.addInstanceRules(rs);
			Environment environment = this.initializationContext.getEnvironment();
			rs.addRule(new ElementSelector("configuration/springProperty"), new SpringPropertyAction(environment));
			rs.addRule(new ElementSelector("*/springProfile"), new SpringProfileAction(environment));
			rs.addRule(new ElementSelector("*/springProfile/*"), new NOPAction());
		}
	}
相关推荐
是老余3 天前
Logback 日志介绍及与Spring Boot 的整合 【保姆级教程】
spring boot·后端·logback
蓝染-惣右介5 天前
【日志框架整合】Slf4j、Log4j、Log4j2、Logback配置模板
单元测试·log4j·logback
MinggeQingchun8 天前
Java - SpringBoot之logback设置日期分割并设置指定时间自动清除,Linux启动运行
logback
( •̀∀•́ )92013 天前
使用 Logback 的最佳实践:`logback.xml` 与 `logback-spring.xml` 的区别与用法
xml·spring·logback
一路奔跑131418 天前
logback日志脱敏后异步写入文件
logback·异步·日志脱敏
white camel19 天前
Java从入门到架构师_日志框架系列
java·logback·log4j2·日志·slf4j
wyh10611519 天前
01 springboot-整合日志(logback-config.xml)
xml·spring boot·logback
小萌新上大分19 天前
logback日志导入使用
java·开发语言·logback·日志·javaee·log·logback在测试中使用
hgSuper19 天前
【Java】java | logback日志配置 | 按包配置级别
java·logback
M00SE21 天前
log4j 和 logback 冲突解决
java·log4j·logback