前言

现在我们来分析一下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() {@Overridepublic 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. 获得LoggerContextLoggerContext loggerContext = getLoggerContext();// 2. 如果LoggerContext中已经配置有org.springframework.boot.logging.LoggingSystem对应的logger,则returnif (isAlreadyInitialized(loggerContext)) {return;}// 3. 调用父类的初始化方法super.beforeInitialize();// 4, 向LoggerContext中的TurboFilterList 添加1个TurboFilter,目的是在LoggerContext没有初始化之前对应打印的日志的请求全部拒绝loggerContext.getTurboFilterList().add(FILTER);// 5. 增加系统属性 : org.jboss.logging.provider-->slf4jconfigureJBossLoggingToUseSlf4j();
      }
      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,则returnLoggerContext 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,默认为%5pLogbackConfigurator configurator = new LogbackConfigurator(context);context.putProperty("LOG_LEVEL_PATTERN",initializationContext.getEnvironment().resolvePlaceholders("${logging.pattern.level:${LOG_LEVEL_PATTERN:%5p}}"));// 3. 实例化DefaultLogbackConfigurationnew 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. 如果存在异常,则抛出IllegalStateExceptionList<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 实例化JoranConfiguratorJoranConfigurator 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. 删除FILTERcontext.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,返回nullif (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,则将其赋值为rootString 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 {@Overridepublic 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. 实例化PropertyResolverthis.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,loggerbase(config);// 2. 实例化consoleAppenderAppender<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和PatternLayoutEncoderConsoleAppender<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. 启动ConsoleAppenderappender.setEncoder(encoder);// 4. 设置appender的名字为CONSOLEconfig.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 获得AnsiElementAnsiElement element = elements.get(getFirstOption());if (element == null) {// 2. 如果没有对应的Element,则根据日志级别获得,如果还是获取不到,则返回GREEN// Assume highlightingelement = 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 源码解析29-LogbackLoggingSystem相关推荐

  1. spring boot 源码解析23-actuate使用及EndPoint解析

    前言 spring boot 中有个很诱人的组件–actuator,可以对spring boot应用做监控,只需在pom文件中加入如下配置即可: <dependency><group ...

  2. spring boot 源码解析52-actuate中MVCEndPoint解析

    前言 之前的几篇文章分析了spring boot 中有关endpoint的实现,细心的朋友可以发现,在org.springframework.boot.actuate.endpoint.mvc 包下也 ...

  3. spring boot 源码解析15-spring mvc零配置

    前言 spring boot 是基于spring 4 的基础上的一个框架,spring 4 有一个新特效–>基于java config 实现零配置.而在企业的实际工作中,spring 都是和sp ...

  4. spring boot 源码解析43-JmxMetricWriter详解

    前言 本文我们来介绍JmxMetricWriter, JmxMetricWriter是依赖于spring jmx 来实现的. 因此本文的开头先介绍一下spring boot 与jmx的集成,然后分析J ...

  5. spring boot 源码解析8-SpringApplication#run第8步

    前言 这篇文章我们分析SpringApplication#run第8步.执行prepareContext方法.该方法的内容比较多.我们慢慢来. 分析 SpringApplication#run 第8步 ...

  6. spring boot 源码解析31-AuthenticationAuditListener,AuthorizationAuditListener

    前言 这篇文章我们来分析一下org.springframework.boot.actuate.security,org.springframework.boot.actuate.audit中的代码,这 ...

  7. Spring Boot源码简析 @EnableTransactionManagement

    相关阅读 Spring Boot源码简析 事务管理 Spring Boot源码简析 @EnableAspectJAutoProxy Spring Boot源码简析 @EnableAsync Sprin ...

  8. 【细读Spring Boot源码】重中之重refresh()

    前言 版本:spring-boot-2.7.3 | spring-context-5.3.22 在Spring Boot启动过程中[细读Spring Boot源码]启动步骤 主流程详情7中applic ...

  9. spring事务源码解析

    前言 在spring jdbcTemplate 事务,各种诡异,包你醍醐灌顶!最后遗留了一个问题:spring是怎么样保证事务一致性的? 当然,spring事务内容挺多的,如果都要讲的话要花很长时间, ...

最新文章

  1. 自然语言处理(NLP)之依存句法分析的可视化及图分析
  2. 英语计算机单词mp3,计算机英语会话(MP3+中英字幕) 第1期:计算机系统(1)
  3. 专家:教育等领域将成为人工智能“用武之地”
  4. 数字图像处理与机器视觉——Visual C++与Matlab实现书中代码勘误
  5. 华为简易压缩算法python_Python(9) --实现一个简单的压缩软件/解压软件的功能
  6. matlab 动态分配内存,[Matlab科学计算之高效编程] 1. 预分配内存
  7. 计算机太卡了怎么解决,电脑太慢太卡怎么办,电脑太慢太卡解决方法
  8. java真实面试题(2)
  9. 文献笔记(7)(2017ISSCC 14.3)
  10. arcgis属性字段fid修改 修改出现bad value
  11. 怎么免费注册微信小程序-微信小程序开发-视频教程1
  12. electron start之后index.html页面不显示
  13. timed out waiting for to be synced
  14. 最近很火火火火的 GitHub 项目
  15. 跨数据中心场景下,kafka集群部署模式
  16. FastDfs与ElasticSearch和Mysql完成海量数据存储搜索功能
  17. Vue+Element 实现订单列表【管理端】02
  18. Zookeeper-3.4.5安装步骤及异常处理
  19. Tomcat网页乱码、控制台乱码
  20. 如何快速的把图片转换为PDF文件格式

热门文章

  1. 买了一台云服务器,到底能用来干嘛?
  2. JWT 实现登录认证 + Token 自动续期方案
  3. 邮箱的格式有哪些,外国人个人、公司都用邮件沟通吗?
  4. 使用Wpf+SDK预览本地摄像头设备
  5. 微信小程序开发个人笔记(2)
  6. ITeye,还IT淫一个简洁的首页吧!
  7. 独家|胡庆勇:态势感知下的智慧指挥(附视频amp;PPT)
  8. NavMesh寻路中的漏斗算法
  9. 你领证了吗?各地2022上半年软考证书发放中...
  10. 【vue报错】【子组件改变父组件数据】 Avoid mutating a prop directly since