SpringBoot 日志系统
- Author: HuiFer
源码阅读仓库: SourceHot-spring-boot
包路径:
org.springframework.boot.logging
日志级别
日志级别:
org.springframework.boot.logging.LogLevelpublic enum LogLevel {TRACE, DEBUG, INFO, WARN, ERROR, FATAL, OFF}
Java 日志实现
org.springframework.boot.logging.java.JavaLoggingSystem
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 对象
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/*** 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
public static LoggingSystem get(ClassLoader classLoader) {// 获取系统属性String loggingSystem = System.getProperty(SYSTEM_PROPERTY);if (StringUtils.hasLength(loggingSystem)) {// 是不是NONEif (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"));}
- 实例化日志系统
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);}}

- 默认日志:
org.springframework.boot.logging.logback.LogbackLoggingSystem
beforeInitialize
初始化之前

链路
org.springframework.boot.context.logging.LoggingApplicationListener#onApplicationEventorg.springframework.boot.context.logging.LoggingApplicationListener#onApplicationStartingEventorg.springframework.boot.logging.LoggingSystem#beforeInitialize
因为前文中我们已知对象是:
org.springframework.boot.logging.logback.LogbackLoggingSystem直接看这个类的beforeInitialize方法@Overridepublic void beforeInitialize() {// 日志上下文LoggerContext loggerContext = getLoggerContext();// 是否初始化if (isAlreadyInitialized(loggerContext)) {return;}// 父类方法super.beforeInitialize();// 添加过滤器loggerContext.getTurboFilterList().add(FILTER);}
初始化之前的的操作完成了初始化方法开始
initialize
org.springframework.boot.context.logging.LoggingApplicationListener#onApplicationEnvironmentPreparedEventprivate 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#initializeSystemprotected 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);}
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 problemSystem.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@Overridepublic 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#initializeWithConventionsprivate void initializeWithConventions(LoggingInitializationContext initializationContext, LogFile logFile) {String config = getSelfInitializationConfig();if (config != null && logFile == null) {// self initialization has occurred, reinitialize in case of property changesreinitialize(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@Overrideprotected 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);}
@Overridepublic void initialize(LoggingInitializationContext initializationContext, String configLocation, LogFile logFile) {LoggerContext loggerContext = getLoggerContext();// 是否加载过if (isAlreadyInitialized(loggerContext)) {return;}// 日志初始化super.initialize(initializationContext, configLocation, logFile);// 删除 FILTERloggerContext.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.");}}
标记
markAsInitializedprivate void markAsInitialized(LoggerContext loggerContext) {loggerContext.putObject(LoggingSystem.class.getName(), new Object());}
此时日志初始化完成。
默认配置文件
getStandardConfigLocations这个方法定义了默认配置文件有哪些。@Overrideprotected String[] getStandardConfigLocations() {return new String[] { "logback-test.groovy", "logback-test.xml", "logback.groovy", "logback.xml" };}
切回
org.springframework.boot.logging.AbstractLoggingSystem#initializeWithConventions方法添加依赖
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-logging</artifactId><version>${revision}</version></dependency>
添加配置文件


此时配置文件地址出现了
protected String getSelfInitializationConfig() {// 寻找配置文件return findConfig(getStandardConfigLocations());}
@Overrideprotected String[] getStandardConfigLocations() {return new String[] { "logback-test.groovy", "logback-test.xml", "logback.groovy", "logback.xml" };}
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
@Overrideprotected void reinitialize(LoggingInitializationContext initializationContext) {// 日志上下文重新设置getLoggerContext().reset();getLoggerContext().getStatusManager().clear();// 加载配置文件loadConfiguration(initializationContext, getSelfInitializationConfig(), null);}
@Overrideprotected 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));}}
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 操作源码不在此进行分析。
