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());
		}
	}
相关推荐
明快de玄米6110 小时前
springboot+logback学习文档
spring boot·logback
zfj3212 天前
java日志框架:slf4j、jul(java.util.logging)、 log4j、 logback
java·log4j·logback·java日志框架·slf4j·jul
灰色孤星A4 天前
瑞吉外卖项目学习笔记(二)Swagger、logback、表单校验和参数打印功能的实现
springboot·logback·swagger·瑞吉外卖·切面编程·表单校验·黑马程序员
赵利伟@4 天前
springboot 配置logback-spring.xml,配置日志只保留7天数据,滚动更新
spring boot·spring·logback
落霞的思绪4 天前
springboot中——Logback介绍
logback
_院长大人_4 天前
Springboot logback 日志打印配置文件,每个日志文件100M,之后滚动到下一个日志文件,日志保留30天(包含traceid)
spring boot·logback
见未见过的风景4 天前
启动报错java.lang.NoClassDefFoundError: ch/qos/logback/core/status/WarnStatus
java·开发语言·logback
m0_7482517214 天前
Spring Boot 3.3.4 升级导致 Logback 之前回滚策略配置不兼容问题解决
java·spring boot·logback
m0_7482329216 天前
Spring Boot 日志 配置 SLF4J 和 Logback
java·spring boot·logback
m0_7482409116 天前
springboot整合Logback
spring boot·后端·logback