SpringBoot 日志系统

日志级别

  • 日志级别: org.springframework.boot.logging.LogLevel

    1
    2
    3
    
    public enum LogLevel {
    	TRACE, DEBUG, INFO, WARN, ERROR, FATAL, OFF
    }
    

Java 日志实现

  • org.springframework.boot.logging.java.JavaLoggingSystem

    image-20200323144523848

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    
    static {
    	// KEY :  springBoot 定义的日志级别, value: jdk 定义的日志级别
    	LEVELS.map(LogLevel.TRACE, Level.FINEST);
    	LEVELS.map(LogLevel.DEBUG, Level.FINE);
    	LEVELS.map(LogLevel.INFO, Level.INFO);
    	LEVELS.map(LogLevel.WARN, Level.WARNING);
    	LEVELS.map(LogLevel.ERROR, Level.SEVERE);
    	LEVELS.map(LogLevel.FATAL, Level.SEVERE);
    	LEVELS.map(LogLevel.OFF, Level.OFF);
    }
    
  • LEVELS 对象

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    
    protected static class LogLevels<T> {
    	/**
    	 * key : SpringBoot 中定义的日志级别, value: 其他日志框架的日志级别
    	 */
    	private final Map<LogLevel, T> systemToNative;
    	/**
    	 * key : 其他日志框架的日志级别 , value: springBoot 中定义中定义的日志级别
    	 */
    	private final Map<T, LogLevel> nativeToSystem;
    }
    

LoggingSystem

  • 抽象类

  • org.springframework.boot.logging.LoggingSystem

  • 一个 map 对象: SYSTEMS

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    
    /**
     * key: 第三方日志框架的类 value: springBoot 中的处理类
     */
    private static final Map<String, String> SYSTEMS;
    
    static {
    	Map<String, String> systems = new LinkedHashMap<>();
    	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);
    }
    
  • 各个抽象方法

    方法名称 作用
    beforeInitialize 初始化之前调用,目的是减少日志输出
    initialize 初始化日志
    cleanUp 清除日志
    getShutdownHandler
    getSupportedLogLevels 获取支持的日志级别
    setLogLevel 设置日志级别
    getLoggerConfigurations 获取日志配置

get

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
public static LoggingSystem get(ClassLoader classLoader) {
	// 获取系统属性
	String loggingSystem = System.getProperty(SYSTEM_PROPERTY);

	if (StringUtils.hasLength(loggingSystem)) {
		// 是不是NONE
		if (NONE.equals(loggingSystem)) {
			// 空的日志系统
			return new NoOpLoggingSystem();
		}
		return get(classLoader, loggingSystem);
	}
	// 循环所有日志,
	return SYSTEMS.entrySet().stream().filter((entry) -> ClassUtils.isPresent(entry.getKey(), classLoader))
			.map((entry) ->
			// 实例化具体日志
				get(classLoader, entry.getValue())).findFirst()
			.orElseThrow(() -> new IllegalStateException("No suitable logging system located"));
}
  • 实例化日志系统
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
private static LoggingSystem get(ClassLoader classLoader, String loggingSystemClass) {
	try {
		Class<?> systemClass = ClassUtils.forName(loggingSystemClass, classLoader);
		Constructor<?> constructor = systemClass.getDeclaredConstructor(ClassLoader.class);
		constructor.setAccessible(true);
		return (LoggingSystem) constructor.newInstance(classLoader);
	}
	catch (Exception ex) {
		throw new IllegalStateException(ex);
	}
}

image-20200323151409473

  • 默认日志: org.springframework.boot.logging.logback.LogbackLoggingSystem

beforeInitialize

  • 初始化之前

    image-20200323154205484

  • 链路

    1. org.springframework.boot.context.logging.LoggingApplicationListener#onApplicationEvent
    2. org.springframework.boot.context.logging.LoggingApplicationListener#onApplicationStartingEvent
    3. org.springframework.boot.logging.LoggingSystem#beforeInitialize
  • 因为前文中我们已知对象是:org.springframework.boot.logging.logback.LogbackLoggingSystem 直接看这个类的 beforeInitialize 方法

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    
    @Override
    public void beforeInitialize() {
    	// 日志上下文
    	LoggerContext loggerContext = getLoggerContext();
    	// 是否初始化
    	if (isAlreadyInitialized(loggerContext)) {
    		return;
    	}
    	// 父类方法
    	super.beforeInitialize();
    	// 添加过滤器
    	loggerContext.getTurboFilterList().add(FILTER);
    }
    
  • 初始化之前的的操作完成了初始化方法开始

initialize

  • org.springframework.boot.context.logging.LoggingApplicationListener#onApplicationEnvironmentPreparedEvent

    1
    2
    3
    4
    5
    6
    
    private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) {
    	if (this.loggingSystem == null) {
    		this.loggingSystem = LoggingSystem.get(event.getSpringApplication().getClassLoader());
    	}
    	initialize(event.getEnvironment(), event.getSpringApplication().getClassLoader());
    }
    
  • org.springframework.boot.context.logging.LoggingApplicationListener#initializeSystem

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    
    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);
    }
    
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    
    private void initializeSystem(ConfigurableEnvironment environment, LoggingSystem system, LogFile logFile) {
    	LoggingInitializationContext initializationContext = new LoggingInitializationContext(environment);
    	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);
    		}
    	}
    }
    
  • org.springframework.boot.logging.logback.LogbackLoggingSystem#initialize

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    
    @Override
    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.");
    	}
    }
    
  • org.springframework.boot.logging.AbstractLoggingSystem#initializeWithConventions

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    
    private void initializeWithConventions(LoggingInitializationContext initializationContext, LogFile logFile) {
    	String config = getSelfInitializationConfig();
    	if (config != null && logFile == null) {
    		// self initialization has occurred, reinitialize in case of property changes
    		reinitialize(initializationContext);
    		return;
    	}
    	if (config == null) {
    		config = getSpringInitializationConfig();
    	}
    	if (config != null) {
    		loadConfiguration(initializationContext, config, logFile);
    		return;
    	}
    	// 加载默认配置
    	loadDefaults(initializationContext, logFile);
    }
    
  • org.springframework.boot.logging.logback.LogbackLoggingSystem#loadDefaults

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    
    @Override
    protected void loadDefaults(LoggingInitializationContext initializationContext, LogFile logFile) {
    	LoggerContext context = getLoggerContext();
    	stopAndReset(context);
    	boolean debug = Boolean.getBoolean("logback.debug");
    	if (debug) {
    		StatusListenerConfigHelper.addOnConsoleListenerInstance(context, new OnConsoleStatusListener());
    	}
    	LogbackConfigurator configurator = debug ? new DebugLogbackConfigurator(context)
    			: new LogbackConfigurator(context);
    	Environment environment = initializationContext.getEnvironment();
    	context.putProperty(LoggingSystemProperties.LOG_LEVEL_PATTERN,
    			environment.resolvePlaceholders("${logging.pattern.level:${LOG_LEVEL_PATTERN:%5p}}"));
    	context.putProperty(LoggingSystemProperties.LOG_DATEFORMAT_PATTERN, environment.resolvePlaceholders(
    			"${logging.pattern.dateformat:${LOG_DATEFORMAT_PATTERN:yyyy-MM-dd HH:mm:ss.SSS}}"));
    	context.putProperty(LoggingSystemProperties.ROLLING_FILE_NAME_PATTERN, environment
    			.resolvePlaceholders("${logging.pattern.rolling-file-name:${LOG_FILE}.%d{yyyy-MM-dd}.%i.gz}"));
    	new DefaultLogbackConfiguration(initializationContext, logFile).apply(configurator);
    	context.setPackagingDataEnabled(true);
    }
    
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    
    @Override
    public void initialize(LoggingInitializationContext initializationContext, String configLocation, LogFile logFile) {
    	LoggerContext loggerContext = getLoggerContext();
    	// 是否加载过
    	if (isAlreadyInitialized(loggerContext)) {
    		return;
    	}
    	// 日志初始化
    	super.initialize(initializationContext, configLocation, logFile);
    	// 删除 FILTER
    	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.");
    	}
    }
    
  • 标记 markAsInitialized

    1
    2
    3
    
    private void markAsInitialized(LoggerContext loggerContext) {
    	loggerContext.putObject(LoggingSystem.class.getName(), new Object());
    }
    

此时日志初始化完成。

默认配置文件

  • getStandardConfigLocations 这个方法定义了默认配置文件有哪些。

    1
    2
    3
    4
    
    @Override
    protected String[] getStandardConfigLocations() {
    	return new String[] { "logback-test.groovy", "logback-test.xml", "logback.groovy", "logback.xml" };
    }
    
  • 切回 org.springframework.boot.logging.AbstractLoggingSystem#initializeWithConventions 方法

  • 添加依赖

    1
    2
    3
    4
    5
    
    <dependency>
    	<groupId>org.springframework.boot</groupId>
    	<artifactId>spring-boot-starter-logging</artifactId>
    	<version>${revision}</version>
    </dependency>
    
  • 添加配置文件

    image-20200323161442058

    image-20200323161522570

  • 此时配置文件地址出现了

    1
    2
    3
    4
    
    protected String getSelfInitializationConfig() {
    	// 寻找配置文件
    	return findConfig(getStandardConfigLocations());
    }
    
    1
    2
    3
    4
    
    @Override
    protected String[] getStandardConfigLocations() {
    	return new String[] { "logback-test.groovy", "logback-test.xml", "logback.groovy", "logback.xml" };
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    
    private String findConfig(String[] locations) {
    	for (String location : locations) {
    		ClassPathResource resource = new ClassPathResource(location, this.classLoader);
    		if (resource.exists()) {
    			return "classpath:" + location;
    		}
    	}
    	return null;
    }
    
  • 此时自定义配置文件如何获取的已经明了。

reinitialize

1
2
3
4
5
6
7
8
@Override
protected void reinitialize(LoggingInitializationContext initializationContext) {
	// 日志上下文重新设置
	getLoggerContext().reset();
	getLoggerContext().getStatusManager().clear();
	// 加载配置文件
	loadConfiguration(initializationContext, getSelfInitializationConfig(), null);
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
@Override
protected void loadConfiguration(LoggingInitializationContext initializationContext, String location,
		LogFile logFile) {
	// 父类方法
	super.loadConfiguration(initializationContext, location, logFile);
	// 获取上下文
	LoggerContext loggerContext = getLoggerContext();
	// 停止并且重启
	stopAndReset(loggerContext);
	try {
		// 配置文件加载
		configureByResourceUrl(initializationContext, loggerContext, ResourceUtils.getURL(location));
	}
	catch (Exception ex) {
		throw new IllegalStateException("Could not initialize Logback logging from " + location, ex);
	}
	List<Status> statuses = loggerContext.getStatusManager().getCopyOfStatusList();
	StringBuilder errors = new StringBuilder();
	for (Status status : statuses) {
		if (status.getLevel() == Status.ERROR) {
			errors.append((errors.length() > 0) ? String.format("%n") : "");
			errors.append(status.toString());
		}
	}
	if (errors.length() > 0) {
		throw new IllegalStateException(String.format("Logback configuration error detected: %n%s", errors));
	}
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
private void configureByResourceUrl(LoggingInitializationContext initializationContext, LoggerContext loggerContext,
		URL url) throws JoranException {
	if (url.toString().endsWith("xml")) {
		// logback 日志操作
		JoranConfigurator configurator = new SpringBootJoranConfigurator(initializationContext);
		// 设置上下文
		configurator.setContext(loggerContext);
		// 执行配置
		configurator.doConfigure(url);
	}
	else {
		new ContextInitializer(loggerContext).configureByResource(url);
	}
}

执行配置属于 logback 操作源码不在此进行分析。