spring boot 源码解析29-LogbackLoggingSystem

前言

现在我们来分析一下LogbackLoggingSystem,spring boot 中默认生效的,该类也是继承自Slf4JLoggingSystem.

解析

LogbackLoggingSystem

  1. 字段,构造器如下:

    // 初始化中如果在环境变量配置有logback.configurationFile,则打印警告日志,提示其使用logging.config进行替代
    private static final String CONFIGURATION_FILE_PROPERTY = "logback.configurationFile";
    
    // 配置LogLevel与Logback的对应关系
    private static final LogLevels<Level> LEVELS = new LogLevels<Level>();
    
    static {
        LEVELS.map(LogLevel.TRACE, Level.TRACE);
        LEVELS.map(LogLevel.TRACE, Level.ALL);
        LEVELS.map(LogLevel.DEBUG, Level.DEBUG);
        LEVELS.map(LogLevel.INFO, Level.INFO);
        LEVELS.map(LogLevel.WARN, Level.WARN);
        LEVELS.map(LogLevel.ERROR, Level.ERROR);
        LEVELS.map(LogLevel.FATAL, Level.ERROR);
        LEVELS.map(LogLevel.OFF, Level.OFF);
    }
    
    // 用于在beforeInitialize方法中,向LoggerContext中的TurboFilterList 添加1个TurboFilter,目的是在LoggerContext没有初始化之前对应打印的日志的请求全部拒绝
    private static final TurboFilter FILTER = new TurboFilter() {
    
        @Override
        public FilterReply decide(Marker marker, ch.qos.logback.classic.Logger logger,
                Level level, String format, Object[] params, Throwable t) {
            return FilterReply.DENY;
        }
    
    };
    
    public LogbackLoggingSystem(ClassLoader classLoader) {
        super(classLoader);
    }
    
  2. 方法如下:

    1. getStandardConfigLocations–> 用于指定默认支持的配置文件.代码如下:

      protected String[] getStandardConfigLocations() {
          return new String[] { "logback-test.groovy", "logback-test.xml", "logback.groovy",
                  "logback.xml" };
      }
    2. beforeInitialize,代码如下:

      public void beforeInitialize() {
          // 1. 获得LoggerContext
          LoggerContext loggerContext = getLoggerContext();
          // 2. 如果LoggerContext中已经配置有org.springframework.boot.logging.LoggingSystem对应的logger,则return
          if (isAlreadyInitialized(loggerContext)) {
              return;
          }
          // 3. 调用父类的初始化方法
          super.beforeInitialize();
          // 4, 向LoggerContext中的TurboFilterList 添加1个TurboFilter,目的是在LoggerContext没有初始化之前对应打印的日志的请求全部拒绝
          loggerContext.getTurboFilterList().add(FILTER);
          // 5. 增加系统属性 : org.jboss.logging.provider-->slf4j
          configureJBossLoggingToUseSlf4j();
      }
      1. 获得LoggerContext
      2. 如果LoggerContext中已经配置有org.springframework.boot.logging.LoggingSystem对应的logger,则return
      3. 调用父类的初始化方法
      4. 向LoggerContext中的TurboFilterList 添加1个TurboFilter,目的是在LoggerContext没有初始化之前对应打印的日志的请求全部拒绝
      5. 增加系统属性 : org.jboss.logging.provider–>slf4j,代码如下:

        private void configureJBossLoggingToUseSlf4j() {
            System.setProperty("org.jboss.logging.provider", "slf4j");
        }
    3. initialize,代码如下:

      public void initialize(LoggingInitializationContext initializationContext,
              String configLocation, LogFile logFile) {
          // 1. 如果LoggerContext中已经配置有org.springframework.boot.logging.LoggingSystem对应的logger,则return
          LoggerContext loggerContext = getLoggerContext();
          if (isAlreadyInitialized(loggerContext)) {
              return;
          }
          // 2. 删除TurboFilter,并调用父类的初始化方法
          loggerContext.getTurboFilterList().remove(FILTER);
          super.initialize(initializationContext, configLocation, logFile);
          // 3. 向loggerContext 添加属性,key为org.springframework.boot.logging.LoggingSystem,value --> object, 意味着已经初始化成功
          markAsInitialized(loggerContext);
          // 4. 如果环境变量配置有logback.configurationFile,则打印警告日志,提示其使用logging.config进行替代
          if (StringUtils.hasText(System.getProperty(CONFIGURATION_FILE_PROPERTY))) {
              getLogger(LogbackLoggingSystem.class.getName()).warn(
                      "Ignoring '" + CONFIGURATION_FILE_PROPERTY + "' system property. "
                              + "Please use 'logging.config' instead.");
          }
      }
      
      1. 如果LoggerContext中已经配置有org.springframework.boot.logging.LoggingSystem对应的logger,则return
      2. 删除TurboFilter,并调用父类的初始化方法
      3. 向loggerContext 添加属性,key为org.springframework.boot.logging.LoggingSystem,value –> object, 意味着已经初始化成功,代码如下:

        private void markAsInitialized(LoggerContext loggerContext) {
            loggerContext.putObject(LoggingSystem.class.getName(), new Object());
        }
      4. 如果环境变量配置有logback.configurationFile,则打印警告日志,提示其使用logging.config进行替代

    4. loadDefaults,该方法在初始化–>配置文件没有时调用.代码如下:

      protected void loadDefaults(LoggingInitializationContext initializationContext,
              LogFile logFile) {
          // 1. 获得LoggerContext 并进行重置
          LoggerContext context = getLoggerContext();
          stopAndReset(context);
          // 2. 实例化LogbackConfigurator,并配置LOG_LEVEL_PATTERN,默认为%5p
          LogbackConfigurator configurator = new LogbackConfigurator(context);
          context.putProperty("LOG_LEVEL_PATTERN",
                  initializationContext.getEnvironment().resolvePlaceholders(
                          "${logging.pattern.level:${LOG_LEVEL_PATTERN:%5p}}"));
          // 3. 实例化DefaultLogbackConfiguration
          new DefaultLogbackConfiguration(initializationContext, logFile)
                  .apply(configurator);
          // 4. 
          context.setPackagingDataEnabled(true);
      }
      1. 获得LoggerContext 并进行重置.代码如下:

        private void stopAndReset(LoggerContext loggerContext) {
            loggerContext.stop();
            loggerContext.reset();
            if (isBridgeHandlerAvailable()) {
                addLevelChangePropagator(loggerContext);
            }
        }
      2. 实例化LogbackConfigurator,并配置LOG_LEVEL_PATTERN,默认为%5p
      3. 实例化DefaultLogbackConfiguration
      4. 设置packagingDataEnabled等于true
    5. loadConfiguration,代码如下:

      protected void loadConfiguration(LoggingInitializationContext initializationContext,
              String location, LogFile logFile) {
          // 1. 
          super.loadConfiguration(initializationContext, location, logFile);
          // 2. 获得LoggerContext 并进行重置
          LoggerContext loggerContext = getLoggerContext();
          stopAndReset(loggerContext);
          try {
              // 3. 进行配置
              configureByResourceUrl(initializationContext, loggerContext,
                      ResourceUtils.getURL(location));
          }
          catch (Exception ex) {
              throw new IllegalStateException(
                      "Could not initialize Logback logging from " + location, ex);
          }
          // 4. 如果存在异常,则抛出IllegalStateException
          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. 调用父类的loadConfiguration,设置系统的环境变量
      2. 获得LoggerContext 并进行重置
      3. 进行配置,代码如下:

        private void configureByResourceUrl(
                LoggingInitializationContext initializationContext,
                LoggerContext loggerContext, URL url) throws JoranException {
            // 1. 如果是xml文件
            if (url.toString().endsWith("xml")) {
                // 1.1 实例化JoranConfigurator
                JoranConfigurator configurator = new SpringBootJoranConfigurator(
                        initializationContext);
                configurator.setContext(loggerContext);
                // 1.2 配置
                configurator.doConfigure(url);
            }
            else {
                // 2. 否则
                new ContextInitializer(loggerContext).configureByResource(url);
            }
        }
        1. 如果是xml文件,

          1. 实例化JoranConfigurator
          2. 配置

          在JoranConfigurator中,最终会回调addInstanceRules,用来处理对xml中的节点的处理,关于这点,我们会在后面进行阐述

        2. 否则,实例化ContextInitializer进行处理,该类是logback自带的
      4. 如果存在异常,则抛出IllegalStateException
    6. cleanUp,代码如下:

      public void cleanUp() {
          // 1. 获得LoggerContext 并从LoggerContext删除org.springframework.boot.logging.LoggingSystem的属性
          LoggerContext context = getLoggerContext();
          markAsUninitialized(context);
          // 2. 
          super.cleanUp();
          // 3. 清空StatusManager中的状态
          context.getStatusManager().clear();
          // 4. 删除FILTER
          context.getTurboFilterList().remove(FILTER);
      }
      1. 获得LoggerContext 并从LoggerContext删除org.springframework.boot.logging.LoggingSystem的属性
      2. 调用父类的cleanUp,删除slf4j 中root logger 配置的所有handler.
      3. 清空StatusManager中的状态
      4. 删除FILTER
    7. reinitialize,代码如下:

      protected void reinitialize(LoggingInitializationContext initializationContext) {
          getLoggerContext().reset();
          getLoggerContext().getStatusManager().clear();
          loadConfiguration(initializationContext, getSelfInitializationConfig(), null);
      }
    8. getLoggerConfigurations,获得所有的LoggerConfiguration.代码如下:

      public List<LoggerConfiguration> getLoggerConfigurations() {
          List<LoggerConfiguration> result = new ArrayList<LoggerConfiguration>();
          // 1. 获得LoggerContext中所有的logger,遍历之
          for (ch.qos.logback.classic.Logger logger : getLoggerContext().getLoggerList()) {
              // 2.获得对应的配置添加到result中
              result.add(getLoggerConfiguration(logger));
          }
          // 3. 排序,将root logger 排在第1位,其他的按照字典顺序排序
          Collections.sort(result, CONFIGURATION_COMPARATOR);
          return result;
      }
      1. 获得LoggerContext中所有的logger,遍历之
      2. 获得对应的配置添加到result中.代码如下:

        public LoggerConfiguration getLoggerConfiguration(String loggerName) {
            return getLoggerConfiguration(getLogger(loggerName));
        }
        1. 根据名字获得对应的Logger,代码如下:

          private ch.qos.logback.classic.Logger getLogger(String name) {
                  LoggerContext factory = getLoggerContext();
                  if (StringUtils.isEmpty(name) || ROOT_LOGGER_NAME.equals(name)) {
                      name = Logger.ROOT_LOGGER_NAME;
                  }
                  return factory.getLogger(name);
              }
          1. 获得LoggerContext
          2. 如果name等于null,空字符串或者等于ROOT,则将其设置为ROOT
          3. 根据name获得对应的Logger
        2. 将logger封装为LoggerConfiguration,代码如下:

          private LoggerConfiguration getLoggerConfiguration(
                  ch.qos.logback.classic.Logger logger) {
              // 1. 如果logger等于null,返回null
              if (logger == null) {
                  return null;
              }
              // 2. 根据logger对应的level,影响的Level分别获得LogLevel,
              LogLevel level = LEVELS.convertNativeToSystem(logger.getLevel());
              LogLevel effectiveLevel = LEVELS
                      .convertNativeToSystem(logger.getEffectiveLevel());
              // 3. 获得logger对应的name,如果name等于null,或者等于root,则将其赋值为root
              String name = logger.getName();
              if (!StringUtils.hasLength(name) || Logger.ROOT_LOGGER_NAME.equals(name)) {
                  name = ROOT_LOGGER_NAME;
              }
              // 4. 实例化LoggerConfiguration进行返回
              return new LoggerConfiguration(name, level, effectiveLevel);
          }
          1. 如果logger等于null,返回null
          2. 根据logger对应的level,影响的Level分别获得LogLevel,
          3. 获得logger对应的name,如果name等于null,或者等于root,则将其赋值为root
          4. 实例化LoggerConfiguration进行返回
      3. 排序,将root logger 排在第1位,其他的按照字典顺序排序
    9. setLogLevel–> 根据给定的loggerName 设置指定的log级别,代码如下:

      public void setLogLevel(String loggerName, LogLevel level) {
          ch.qos.logback.classic.Logger logger = getLogger(loggerName);
          if (logger != null) {
              logger.setLevel(LEVELS.convertSystemToNative(level));
          }
      }
      1. 根据loggerName 获得对应的Logger
      2. 如果Logger不等于null,则首先将LogLevel 转换为logback的对应的级别,然后进行设置即可
    10. getShutdownHandler–> 在run方法中调用了LoggerContext#stop,代码如下:

      public Runnable getShutdownHandler() {
          return new ShutdownHandler(); // 在run方法中调用了LoggerContext#stop
      }

      ShutdownHandler代码如下:

      private final class ShutdownHandler implements Runnable {
      
          @Override
          public void run() {
              getLoggerContext().stop();
          }
      }

LogbackLoggingSystem生命周期

  1. ApplicationStartingEvent事件处理 执行Log4J2LoggingSystem#beforeInitialize方法.在该方法中会调用LogbackLoggingSystem#configureJdkLoggingBridgeHandler,会判断org.slf4j.bridge.SLF4JBridgeHandler是否存在,此时由于加入了spring-boot-starter-logging,因此加入了jul-to-slf4 jar 包,因此会为root logger添加SLF4JBridgeHandler.然后执行后续操作:

    1. 向LoggerContext中的TurboFilterList 添加1个TurboFilter,目的是在LoggerContext没有初始化之前对应打印的日志的请求全部拒绝
    2. 增加系统属性 : org.jboss.logging.provider–>slf4j
  2. ApplicationEnvironmentPreparedEvent 最终会执行LogbackLoggingSystem#initialize,在该方法中会调用AbstractLoggingSystem#initialize , 此时由于默认情况下没有配置logging.config 属性,因此最终会回调LogbackLoggingSystem#loadDefaults,在该方法中进行配置.

  3. ApplicationPreparedEvent 和其他的LoggingSystem一样,都是进行注册

  4. ContextClosedEvent 最终会调用LogbackLoggingSystem#cleanUp,处理逻辑如下:

    1. 获得LoggerContext 并从LoggerContext删除org.springframework.boot.logging.LoggingSystem的属性
    2. 调用父类的cleanUp方法,最终会调用SLF4JBridgeHandler#removeHandlersForRootLogger方法,删除root loger 的hanler
    3. 清空StatusManager中的状态
    4. 删除FILTER
  5. 这步要是生效,需要配置logging.register-shutdown-hook=true.默认不生效.如果配置的话,就会执行LogbackLoggingSystem 中声明的ShutdownHandler的run方法,调用LoggerContext#stop.

DefaultLogbackConfiguration

该类是在spring boot中对logback的默认配置.没有使用常规的xml文件的方式来实现,而是通过该类进行api的配置,目的是改善启动时间.

  1. 该类的字段如下:

    // console 日志格式
    private static final String CONSOLE_LOG_PATTERN = "%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} "
            + "%clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} "
            + "%clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} "
            + "%clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}";
    
    // 文件的 日志格式,需要配置logging.file或者logging.path 才生效
    private static final String FILE_LOG_PATTERN = "%d{yyyy-MM-dd HH:mm:ss.SSS} "
            + "${LOG_LEVEL_PATTERN:-%5p} ${PID:- } --- [%t] %-40.40logger{39} : %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}";
    
    // 字符集,默认utf-8
    private static final Charset UTF8 = Charset.forName("UTF-8");
    
    private final PropertyResolver patterns;
    
    // 配置有logging.file或者logging.path,才不为空
    private final LogFile logFile;

    其中CONSOLE_LOG_PATTERN 的解释如下:

    %clr:ColorConverter 进行处理
    %d{yyyy-MM-dd HH:mm:ss.SSS}: 输出时间
    ${LOG_LEVEL_PATTERN:-%5p}: 日志级别,并且使用5个字符靠左对齐
    ${PID:- }:线程id
    %15.15t:t-->线程 %15.15 如果线程名小于15个字符,则在左边填充空格.如果大于15个字符,则进行截断
    %-40.40logger{39}: 日志输出者的名字,-40.40 : 如果线程名小于40个字符,则在右边填充空格.如果大于40个字符,则进行截断
    %m:日志
    %n:换行符
    {LOG_EXCEPTION_CONVERSION_WORD:-%wEx}:ExtendedWhitespaceThrowableProxyConverter 进行处理

    参考链接: Chapter 6: Layouts

    构造器如下:

    DefaultLogbackConfiguration(LoggingInitializationContext initializationContext,
            LogFile logFile) {
        // 1. 实例化PropertyResolver
        this.patterns = getPatternsResolver(initializationContext.getEnvironment());
        this.logFile = logFile;
    }

    调用getPatternsResolver方法进行实例化.代码如下:

    private PropertyResolver getPatternsResolver(Environment environment) {
        if (environment == null) {
            return new PropertySourcesPropertyResolver(null);
        }
        return RelaxedPropertyResolver.ignoringUnresolvableNestedPlaceholders(environment,
                "logging.pattern.");
    }
    1. 如果Environment等null,则直接返回PropertySourcesPropertyResolver
    2. 返回RelaxedPropertyResolver,读取Environment中以logging.pattern. 开头的配置.默认是在第2步返回的
  2. apply方法如下:

    public void apply(LogbackConfigurator config) {
        synchronized (config.getConfigurationLock()) {
            // 1. 设置conversionRule,logger
            base(config);
            // 2. 实例化consoleAppender
            Appender<ILoggingEvent> consoleAppender = consoleAppender(config);
            // 3. 如果logFile 不等于null,
            if (this.logFile != null) {
                // 3.1 则创建fileAppender,添加到rootLogger中
                Appender<ILoggingEvent> fileAppender = fileAppender(config,
                        this.logFile.toString());
                config.root(Level.INFO, consoleAppender, fileAppender);
            }
            else {
                // 否则,只添加consoleAppender到rootLogger中
                config.root(Level.INFO, consoleAppender);
            }
        }
    }
    
    1. 设置conversionRule,logger.代码如下:

      private void base(LogbackConfigurator config) {
          config.conversionRule("clr", ColorConverter.class);
          config.conversionRule("wex", WhitespaceThrowableProxyConverter.class);
          config.conversionRule("wEx", ExtendedWhitespaceThrowableProxyConverter.class);
          config.logger("org.apache.catalina.startup.DigesterFactory", Level.ERROR);
          config.logger("org.apache.catalina.util.LifecycleBase", Level.ERROR);
          config.logger("org.apache.coyote.http11.Http11NioProtocol", Level.WARN);
          config.logger("org.apache.sshd.common.util.SecurityUtils", Level.WARN);
          config.logger("org.apache.tomcat.util.net.NioSelectorPool", Level.WARN);
          config.logger("org.crsh.plugin", Level.WARN);
          config.logger("org.crsh.ssh", Level.WARN);
          config.logger("org.eclipse.jetty.util.component.AbstractLifeCycle", Level.ERROR);
          config.logger("org.hibernate.validator.internal.util.Version", Level.WARN);
          config.logger("org.springframework.boot.actuate.autoconfigure."
                  + "CrshAutoConfiguration", Level.WARN);
          config.logger("org.springframework.boot.actuate.endpoint.jmx", null, false,
                  debugRemapAppender);
          config.logger("org.thymeleaf", null, false, debugRemapAppender);
      }
      1. 设置conversionRule.相当于在spring-boot/src/main/resources/org/springframework/boot/logging/logback/defaults.xml 中的如下配置:

        <conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter" />
        <conversionRule conversionWord="wex" converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter" />
        <conversionRule conversionWord="wEx" converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter" />
      2. 实例化LevelRemappingAppender,并启动LevelRemappingAppender–>只是将LevelRemappingAppender的父类中(AppenderBase)的started 设置为true.添加appender.相当于在spring-boot/src/main/resources/org/springframework/boot/logging/logback/defaults.xml 中的如下配置 :

        <appender name="DEBUG_LEVEL_REMAPPER" class="org.springframework.boot.logging.logback.LevelRemappingAppender">
        <destinationLogger>org.springframework.boot</destinationLogger>
        </appender>
      3. 添加1系列的logger.相当于在spring-boot/src/main/resources/org/springframework/boot/logging/logback/defaults.xml 中的如下配置:

        <logger name="org.apache.catalina.startup.DigesterFactory" level="ERROR"/>
        <logger name="org.apache.catalina.util.LifecycleBase" level="ERROR"/>
        <logger name="org.apache.coyote.http11.Http11NioProtocol" level="WARN"/>
        <logger name="org.apache.sshd.common.util.SecurityUtils" level="WARN"/>
        <logger name="org.apache.tomcat.util.net.NioSelectorPool" level="WARN"/>
        <logger name="org.crsh.plugin" level="WARN"/>
        <logger name="org.crsh.ssh" level="WARN"/>
        <logger name="org.eclipse.jetty.util.component.AbstractLifeCycle" level="ERROR"/>
        <logger name="org.hibernate.validator.internal.util.Version" level="WARN"/>
        <logger name="org.springframework.boot.actuate.autoconfigure.CrshAutoConfiguration" level="WARN"/>
        <logger name="org.springframework.boot.actuate.endpoint.jmx" additivity="false">
            <appender-ref ref="DEBUG_LEVEL_REMAPPER"/>
        </logger>
        <logger name="org.thymeleaf" additivity="false">
            <appender-ref ref="DEBUG_LEVEL_REMAPPER"/>
        </logger>
    2. 实例化consoleAppender,代码如下:

      private Appender<ILoggingEvent> consoleAppender(LogbackConfigurator config) {
          // 1. 实例化ConsoleAppender和PatternLayoutEncoder
          ConsoleAppender<ILoggingEvent> appender = new ConsoleAppender<ILoggingEvent>();
          PatternLayoutEncoder encoder = new PatternLayoutEncoder();
          // 2. 获取logging.pattern.console的配置,如果获取不到则使用CONSOLE_LOG_PATTERN,然后设置到ConsoleAppender 输出格式中
          String logPattern = this.patterns.getProperty("console", CONSOLE_LOG_PATTERN);
          encoder.setPattern(OptionHelper.substVars(logPattern, config.getContext()));
          encoder.setCharset(UTF8);
          config.start(encoder);
          // 3. 启动ConsoleAppender
          appender.setEncoder(encoder);
          // 4. 设置appender的名字为CONSOLE
          config.appender("CONSOLE", appender);
          return appender;
      }
      1. 实例化ConsoleAppender和PatternLayoutEncoder
      2. 获取logging.pattern.console的配置,如果获取不到则使用CONSOLE_LOG_PATTERN,然后设置到ConsoleAppender 输出格式中
      3. 启动ConsoleAppender
      4. 设置appender的名字为CONSOLE

      相当于spring-boot/src/main/resources/org/springframework/boot/logging/logback/console-appender.xml,如下:

      <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
          <encoder>
              <pattern>${CONSOLE_LOG_PATTERN}</pattern>
              <charset>utf8</charset>
          </encoder>
      </appender>
    3. 如果logFile 不等于null

      1. 则创建fileAppender,添加到rootLogger中.相当于如下配置:

        <root level="INFO">
            <appender-ref ref="CONSOLE" />
            <appender-ref ref="FILE" />
        </root>
      2. 否则只添加consoleAppender到rootLogger中.相当于如下配置:

        <root level="INFO">
            <appender-ref ref="CONSOLE" />
        </root>

ColorConverter

该类用来处理颜色

  1. 字段如下:

    // key --> logback.xml 中配置的颜色,value --> AnsiElement
    private static final Map<String, AnsiElement> elements;
    
    static {
        Map<String, AnsiElement> ansiElements = new HashMap<String, AnsiElement>();
        ansiElements.put("faint", AnsiStyle.FAINT);
        ansiElements.put("red", AnsiColor.RED);
        ansiElements.put("green", AnsiColor.GREEN);
        ansiElements.put("yellow", AnsiColor.YELLOW);
        ansiElements.put("blue", AnsiColor.BLUE);
        ansiElements.put("magenta", AnsiColor.MAGENTA);
        ansiElements.put("cyan", AnsiColor.CYAN);
        elements = Collections.unmodifiableMap(ansiElements);
    }
    
    // key--> 日志级别,value-->AnsiElement
    private static final Map<Integer, AnsiElement> levels;
    
    static {
        Map<Integer, AnsiElement> ansiLevels = new HashMap<Integer, AnsiElement>();
        ansiLevels.put(Level.ERROR_INTEGER, AnsiColor.RED);
        ansiLevels.put(Level.WARN_INTEGER, AnsiColor.YELLOW);
        levels = Collections.unmodifiableMap(ansiLevels);
    }
  2. transform,该方法会再打印日志时回调,代码如下:

    protected String transform(ILoggingEvent event, String in) {
        // getFirstOption--> 返回第1个选项,等于null,意味这里没有进行配置
        // 1. 通过elements根据Option 获得AnsiElement
        AnsiElement element = elements.get(getFirstOption());
        if (element == null) {
            // 2. 如果没有对应的Element,则根据日志级别获得,如果还是获取不到,则返回GREEN
            // Assume highlighting
            element = levels.get(event.getLevel().toInteger());
            element = (element == null ? AnsiColor.GREEN : element);
        }
        // 3. 转成string 
        return toAnsiString(in, element);
    }
    1. 通过elements根据Option 获得AnsiElement
    2. 如果没有对应的Element,则根据日志级别获得,如果还是获取不到,则返回GREEN
    3. 转成string ,代码如下:

      protected String toAnsiString(String in, AnsiElement element) {
          return AnsiOutput.toString(element, in);
      }

    详细说明,以如下一条日志为例:

    2018-01-22 16:38:41.812 INFO 55134 — [ main] com.example.demo.DemoApplication : The following profiles are active: test

    1. 由于 2018-01-22 16:38:41.812 属于默认配置的日志格式中的%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} 因此会在transform方法的第1步获得AnsiStyle.FAINT
    2. 由于 INFO 属于默认配置的日志格式中的%clr(${LOG_LEVEL_PATTERN:-%5p}) 由于没有配置颜色,同时在ColorConverter中也没有配置info级别所对应的颜色,因此返回的是AnsiColor.GREEN
    3. 由于 55134 属于默认配置的日志格式中的%clr(${PID:- }){magenta} 因此会在transform方法返回AnsiColor.MAGENTA
    4. 由于 — 属于默认配置的日志格式中的%clr(—){faint} 因此会在transform方法返回AnsiStyle.FAIN
    5. 由于 [ main] 属于默认配置的日志格式中的%clr([%15.15t]){faint} 因此会在transform方法返回AnsiStyle.FAIN
    6. 由于 com.example.demo.DemoApplication 属于默认配置的日志格式中的%clr(%-40.40logger{39}){cyan} 因此会在transform方法返回AnsiColor.CYAN
    7. 由于 : 属于默认配置的日志格式中的%clr(:){faint} 因此会在transform方法返回AnsiStyle.FAIN

ExtendedWhitespaceThrowableProxyConverter

该类是在异常堆栈的打印过程中添加一些空格.

  1. throwableProxyToString–> 在打印日志的过程中会调用.代码如下:

    protected String throwableProxyToString(IThrowableProxy tp) {
        return CoreConstants.LINE_SEPARATOR + super.throwableProxyToString(tp)
                + CoreConstants.LINE_SEPARATOR;
    }

    其实就是在堆栈的日志前后加上换行符

    原文作者:Spring Boot
    原文地址: https://blog.csdn.net/qq_26000415/article/details/79130943
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞