SpringBoot 日志系统

日志级别

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

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

Java 日志实现

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

    image-20200323144523848

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

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

LoggingSystem

  • 抽象类
  • org.springframework.boot.logging.LoggingSystem

  • 一个 map 对象: SYSTEMS

    1. /**
    2. * key: 第三方日志框架的类 value: springBoot 中的处理类
    3. */
    4. private static final Map<String, String> SYSTEMS;
    5. static {
    6. Map<String, String> systems = new LinkedHashMap<>();
    7. systems.put("ch.qos.logback.core.Appender", "org.springframework.boot.logging.logback.LogbackLoggingSystem");
    8. systems.put("org.apache.logging.log4j.core.impl.Log4jContextFactory",
    9. "org.springframework.boot.logging.log4j2.Log4J2LoggingSystem");
    10. systems.put("java.util.logging.LogManager", "org.springframework.boot.logging.java.JavaLoggingSystem");
    11. SYSTEMS = Collections.unmodifiableMap(systems);
    12. }
  • 各个抽象方法

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

get

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

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

initialize

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

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

    1. protected void initialize(ConfigurableEnvironment environment, ClassLoader classLoader) {
    2. new LoggingSystemProperties(environment).apply();
    3. this.logFile = LogFile.get(environment);
    4. if (this.logFile != null) {
    5. this.logFile.applyToSystemProperties();
    6. }
    7. this.loggerGroups = new LoggerGroups(DEFAULT_GROUP_LOGGERS);
    8. // 早期 的日志级别
    9. initializeEarlyLoggingLevel(environment);
    10. // 初始化日志系统
    11. initializeSystem(environment, this.loggingSystem, this.logFile);
    12. // 初始化日志级别
    13. initializeFinalLoggingLevels(environment, this.loggingSystem);
    14. registerShutdownHookIfNecessary(environment, this.loggingSystem);
    15. }
    1. private void initializeSystem(ConfigurableEnvironment environment, LoggingSystem system, LogFile logFile) {
    2. LoggingInitializationContext initializationContext = new LoggingInitializationContext(environment);
    3. String logConfig = environment.getProperty(CONFIG_PROPERTY);
    4. if (ignoreLogConfig(logConfig)) {
    5. // 日志系统初始化
    6. system.initialize(initializationContext, null, logFile);
    7. }
    8. else {
    9. try {
    10. ResourceUtils.getURL(logConfig).openStream().close();
    11. system.initialize(initializationContext, logConfig, logFile);
    12. }
    13. catch (Exception ex) {
    14. // NOTE: We can't use the logger here to report the problem
    15. System.err.println("Logging system failed to initialize using configuration from '" + logConfig + "'");
    16. ex.printStackTrace(System.err);
    17. throw new IllegalStateException(ex);
    18. }
    19. }
    20. }
  • org.springframework.boot.logging.logback.LogbackLoggingSystem#initialize

    1. @Override
    2. public void initialize(LoggingInitializationContext initializationContext, String configLocation, LogFile logFile) {
    3. LoggerContext loggerContext = getLoggerContext();
    4. if (isAlreadyInitialized(loggerContext)) {
    5. return;
    6. }
    7. // 日志初始化
    8. super.initialize(initializationContext, configLocation, logFile);
    9. loggerContext.getTurboFilterList().remove(FILTER);
    10. markAsInitialized(loggerContext);
    11. if (StringUtils.hasText(System.getProperty(CONFIGURATION_FILE_PROPERTY))) {
    12. getLogger(LogbackLoggingSystem.class.getName()).warn("Ignoring '" + CONFIGURATION_FILE_PROPERTY
    13. + "' system property. Please use 'logging.config' instead.");
    14. }
    15. }
  • org.springframework.boot.logging.AbstractLoggingSystem#initializeWithConventions

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

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

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

此时日志初始化完成。

默认配置文件

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

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

  • 添加依赖

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

    image-20200323161442058

    image-20200323161522570

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

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

reinitialize

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

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