# SpringBoot 日志系统
- Author: [HuiFer](https://github.com/huifer)
- 源码阅读仓库: [SourceHot-spring-boot](https://github.com/SourceHot/spring-boot-read)

- 包路径: `org.springframework.boot.logging`

## 日志级别
- 日志级别: `org.springframework.boot.logging.LogLevel`
```java
public enum LogLevel {
	TRACE, DEBUG, INFO, WARN, ERROR, FATAL, OFF
}
```





## java 日志实现

- `org.springframework.boot.logging.java.JavaLoggingSystem`

![image-20200323144523848](/images/SpringBoot/image-20200323144523848.png)

```JAVA
	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 对象

  ```java
  	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`

```JAVA
	/**
	 * 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

```java
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"));
	}
```

- 实例化日志系统

```java
	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](/images/SpringBoot/image-20200323151409473.png)



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

### beforeInitialize

- 初始化之前

  ![image-20200323154205484](/images/SpringBoot/image-20200323154205484.png)

  - 链路
    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`**方法

  ```JAVA
  	@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`

  ```JAVA
  	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`

  ```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);
  	}
  
  ```

  ```JAVA
  	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`

```java
	@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`

  ```JAVA
  	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` 

    ```JAVA
    	@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);
    	}
    
    ```





```JAVA
	@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`

```JAVA
	private void markAsInitialized(LoggerContext loggerContext) {
		loggerContext.putObject(LoggingSystem.class.getName(), new Object());
	}

```



此时日志初始化完成





### 默认配置文件

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

```java
	@Override
	protected String[] getStandardConfigLocations() {
		return new String[] { "logback-test.groovy", "logback-test.xml", "logback.groovy", "logback.xml" };
	}

```





- 切回`org.springframework.boot.logging.AbstractLoggingSystem#initializeWithConventions`方法



- 添加依赖

```XML
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-logging</artifactId>
			<version>${revision}</version>
		</dependency>

```

- 添加配置文件

![image-20200323161442058](/images/SpringBoot/image-20200323161442058.png)





![image-20200323161522570](/images/SpringBoot/image-20200323161522570.png)

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



```JAVA
	protected String getSelfInitializationConfig() {
	    // 寻找配置文件
		return findConfig(getStandardConfigLocations());
	}

```

```JAVA
	@Override
	protected String[] getStandardConfigLocations() {
		return new String[] { "logback-test.groovy", "logback-test.xml", "logback.groovy", "logback.xml" };
	}

```

```JAVA
	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

```JAVA
	@Override
	protected void reinitialize(LoggingInitializationContext initializationContext) {
		// 日志上下文重新设置
		getLoggerContext().reset();
		getLoggerContext().getStatusManager().clear();
		// 加载配置文件
		loadConfiguration(initializationContext, getSelfInitializationConfig(), null);
	}

```



```JAVA
	@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));
		}
	}

```



```JAVA
	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 操作源码不在此进行分析