首发于编程之路
Spring Boot中logback配置文件加载过程

Spring Boot中logback配置文件加载过程

这里不会列举Spring Boot中logback配置语法,网上很多。

这里主要过一下logback在Spring Boot应用中是如何初始化的,以及加载logback相关配置文件的顺序。

先列出几点:

  • 如果classpath下有logback-test.xml会优先生效,xxx-spring.xml优先级最低。
  • 日志系统初始化之前日志输出有DEBUG,因为此刻logback-spring还未生效,spring boot默认日志级别是DEBUG。
  • 有些依赖的库日志输出不受控制,是因为最终使用的logger不是logback,比如使用的log4j。
  • Spring Boot使用JUnit测试时使用SpringBootTest注解与否在日志系统初始化时有区别。

下面通过浏览代码看看具体的过程。

在Spring应用启动时会先获取到对应的日志实例。

// org.springframework.boot.logging.LoggingApplicationListener
private void onApplicationStartedEvent(ApplicationStartedEvent event) {
	this.loggingSystem = LoggingSystem
			.get(event.getSpringApplication().getClassLoader());
	this.loggingSystem.beforeInitialize();
}

检测和获取日志系统,检测的顺序定义在SYSTEMS中,最终通过反射创建LogbackLoggingSystem实例。

// org.springframework.boot.logging.LoggingSystem
public static LoggingSystem get(ClassLoader classLoader) {
	String loggingSystem = System.getProperty(SYSTEM_PROPERTY);// null
	if (StringUtils.hasLength(loggingSystem)) {
		if (NONE.equals(loggingSystem)) {
			return new NoOpLoggingSystem();
		}
		return get(classLoader, loggingSystem);
	}
	for (Map.Entry<String, String> entry : SYSTEMS.entrySet()) {
		if (ClassUtils.isPresent(entry.getKey(), classLoader)) {
			return get(classLoader, entry.getValue());
		}
	}
	throw new IllegalStateException("No suitable logging system located");
}


/**
 * A System property that can be used to indicate the {@link LoggingSystem} to use.
 */
public static final String SYSTEM_PROPERTY = LoggingSystem.class.getName();

private static final Map<String, String> SYSTEMS;

static {
	Map<String, String> systems = new LinkedHashMap<String, String>();
	systems.put("ch.qos.logback.core.Appender",
			"org.springframework.boot.logging.logback.LogbackLoggingSystem");
	systems.put("org.apache.logging.log4j.core.impl.Log4jContextFactory",
			"org.springframework.boot.logging.log4j2.Log4J2LoggingSystem");
	systems.put("java.util.logging.LogManager",
			"org.springframework.boot.logging.java.JavaLoggingSystem");
	SYSTEMS = Collections.unmodifiableMap(systems);
}

然后在ApplicationEnvironmentPreparedEvent之后进行logback的初始化。

private void onApplicationEnvironmentPreparedEvent(
		ApplicationEnvironmentPreparedEvent event) {
	if (this.loggingSystem == null) {
		this.loggingSystem = LoggingSystem
				.get(event.getSpringApplication().getClassLoader());
	}
	initialize(event.getEnvironment(), event.getSpringApplication().getClassLoader());
}

	/**
 * Initialize the logging system according to preferences expressed through the
 * {@link Environment} and the classpath.
 * @param environment the environment
 * @param classLoader the classloader
 */
protected void initialize(ConfigurableEnvironment environment,
		ClassLoader classLoader) {
	new LoggingSystemProperties(environment).apply();
	LogFile logFile = LogFile.get(environment);
	if (logFile != null) {
		logFile.applyToSystemProperties();
	}
	initializeEarlyLoggingLevel(environment);
	initializeSystem(environment, this.loggingSystem, logFile);
	initializeFinalLoggingLevels(environment, this.loggingSystem);
	registerShutdownHookIfNecessary(environment, this.loggingSystem);
}

重点看配置文件检测的过程。

private void initializeWithConventions(
		LoggingInitializationContext initializationContext, LogFile logFile) {
		// 获取logback自己支持的配置文件
	String config = getSelfInitializationConfig();
	if (config != null && logFile == null) {
		// self initialization has occurred, reinitialize in case of property changes
		reinitialize(initializationContext);
		return;
	}
	// 如果没有则检测spring相关的logback配置
	if (config == null) {
		config = getSpringInitializationConfig();
	}
	if (config != null) {
		loadConfiguration(initializationContext, config, logFile);
		return;
	}
	loadDefaults(initializationContext, logFile);
}

logback自身配置文件的生效顺序。

// org.springframework.boot.logging.logback.LogbackLoggingSystem
@Override
protected String[] getStandardConfigLocations() {
	return new String[] { "logback-test.groovy", "logback-test.xml", "logback.groovy",
			"logback.xml" };
}

通过这里可以看到spring boot中支持的logback配置文件格式,就是在logback自配置文件(logback-test.xml, logback.xml等)基础上文件名后面加了“-spring”,如logback-test-spring, logback-spring等。

/**
 * Return the spring config locations for this system. By default this method returns
 * a set of locations based on {@link #getStandardConfigLocations()}.
 * @return the spring config locations
 * @see #getSpringInitializationConfig()
 */
protected String[] getSpringConfigLocations() {
	String[] locations = getStandardConfigLocations();
	for (int i = 0; i < locations.length; i++) {
		String extension = StringUtils.getFilenameExtension(locations[i]);
		locations[i] = locations[i].substring(0,
				locations[i].length() - extension.length() - 1) + "-spring."
				+ extension;
	}
	return locations;
}

此外,Spring Boot使用JUnit时,如果没有配置SpringBootTest注解,日志系统根本不会得到初始化,会使用org.slf4j.impl.StaticLoggerBinder获取,如果在test/resource下面存在logback-test.xml则会生效,否则就使用系统默认的配置。如果配置了置SpringBootTest注解,则SpringBoot会正常的初始化,日志系统会正常加载。

参考

Configuration in logback

Spring Boot Logging

编辑于 2017-07-13 21:33