前言
现在我们来分析一下LogbackLoggingSystem,spring boot 中默认生效的,该类也是继承自Slf4JLoggingSystem.
解析
LogbackLoggingSystem
字段,构造器如下:
// 初始化中如果在环境变量配置有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); }
方法如下:
getStandardConfigLocations–> 用于指定默认支持的配置文件.代码如下:
protected String[] getStandardConfigLocations() { return new String[] { "logback-test.groovy", "logback-test.xml", "logback.groovy", "logback.xml" }; }
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(); }
- 获得LoggerContext
- 如果LoggerContext中已经配置有org.springframework.boot.logging.LoggingSystem对应的logger,则return
- 调用父类的初始化方法
- 向LoggerContext中的TurboFilterList 添加1个TurboFilter,目的是在LoggerContext没有初始化之前对应打印的日志的请求全部拒绝
增加系统属性 : org.jboss.logging.provider–>slf4j,代码如下:
private void configureJBossLoggingToUseSlf4j() { System.setProperty("org.jboss.logging.provider", "slf4j"); }
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."); } }
- 如果LoggerContext中已经配置有org.springframework.boot.logging.LoggingSystem对应的logger,则return
- 删除TurboFilter,并调用父类的初始化方法
向loggerContext 添加属性,key为org.springframework.boot.logging.LoggingSystem,value –> object, 意味着已经初始化成功,代码如下:
private void markAsInitialized(LoggerContext loggerContext) { loggerContext.putObject(LoggingSystem.class.getName(), new Object()); }
如果环境变量配置有logback.configurationFile,则打印警告日志,提示其使用logging.config进行替代
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); }
获得LoggerContext 并进行重置.代码如下:
private void stopAndReset(LoggerContext loggerContext) { loggerContext.stop(); loggerContext.reset(); if (isBridgeHandlerAvailable()) { addLevelChangePropagator(loggerContext); } }
- 实例化LogbackConfigurator,并配置LOG_LEVEL_PATTERN,默认为%5p
- 实例化DefaultLogbackConfiguration
- 设置packagingDataEnabled等于true
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)); } }
- 调用父类的loadConfiguration,设置系统的环境变量
- 获得LoggerContext 并进行重置
进行配置,代码如下:
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); } }
如果是xml文件,
- 实例化JoranConfigurator
- 配置
在JoranConfigurator中,最终会回调addInstanceRules,用来处理对xml中的节点的处理,关于这点,我们会在后面进行阐述
- 否则,实例化ContextInitializer进行处理,该类是logback自带的
- 如果存在异常,则抛出IllegalStateException
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); }
- 获得LoggerContext 并从LoggerContext删除org.springframework.boot.logging.LoggingSystem的属性
- 调用父类的cleanUp,删除slf4j 中root logger 配置的所有handler.
- 清空StatusManager中的状态
- 删除FILTER
reinitialize,代码如下:
protected void reinitialize(LoggingInitializationContext initializationContext) { getLoggerContext().reset(); getLoggerContext().getStatusManager().clear(); loadConfiguration(initializationContext, getSelfInitializationConfig(), null); }
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; }
- 获得LoggerContext中所有的logger,遍历之
获得对应的配置添加到result中.代码如下:
public LoggerConfiguration getLoggerConfiguration(String loggerName) { return getLoggerConfiguration(getLogger(loggerName)); }
根据名字获得对应的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); }
- 获得LoggerContext
- 如果name等于null,空字符串或者等于ROOT,则将其设置为ROOT
- 根据name获得对应的Logger
将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); }
- 如果logger等于null,返回null
- 根据logger对应的level,影响的Level分别获得LogLevel,
- 获得logger对应的name,如果name等于null,或者等于root,则将其赋值为root
- 实例化LoggerConfiguration进行返回
- 排序,将root logger 排在第1位,其他的按照字典顺序排序
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)); } }
- 根据loggerName 获得对应的Logger
- 如果Logger不等于null,则首先将LogLevel 转换为logback的对应的级别,然后进行设置即可
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生命周期
ApplicationStartingEvent事件处理 执行Log4J2LoggingSystem#beforeInitialize方法.在该方法中会调用LogbackLoggingSystem#configureJdkLoggingBridgeHandler,会判断org.slf4j.bridge.SLF4JBridgeHandler是否存在,此时由于加入了spring-boot-starter-logging,因此加入了jul-to-slf4 jar 包,因此会为root logger添加SLF4JBridgeHandler.然后执行后续操作:
- 向LoggerContext中的TurboFilterList 添加1个TurboFilter,目的是在LoggerContext没有初始化之前对应打印的日志的请求全部拒绝
- 增加系统属性 : org.jboss.logging.provider–>slf4j
ApplicationEnvironmentPreparedEvent 最终会执行LogbackLoggingSystem#initialize,在该方法中会调用AbstractLoggingSystem#initialize , 此时由于默认情况下没有配置logging.config 属性,因此最终会回调LogbackLoggingSystem#loadDefaults,在该方法中进行配置.
ApplicationPreparedEvent 和其他的LoggingSystem一样,都是进行注册
ContextClosedEvent 最终会调用LogbackLoggingSystem#cleanUp,处理逻辑如下:
- 获得LoggerContext 并从LoggerContext删除org.springframework.boot.logging.LoggingSystem的属性
- 调用父类的cleanUp方法,最终会调用SLF4JBridgeHandler#removeHandlersForRootLogger方法,删除root loger 的hanler
- 清空StatusManager中的状态
- 删除FILTER
这步要是生效,需要配置logging.register-shutdown-hook=true.默认不生效.如果配置的话,就会执行LogbackLoggingSystem 中声明的ShutdownHandler的run方法,调用LoggerContext#stop.
DefaultLogbackConfiguration
该类是在spring boot中对logback的默认配置.没有使用常规的xml文件的方式来实现,而是通过该类进行api的配置,目的是改善启动时间.
该类的字段如下:
// 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."); }
- 如果Environment等null,则直接返回PropertySourcesPropertyResolver
- 返回RelaxedPropertyResolver,读取Environment中以logging.pattern. 开头的配置.默认是在第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); } } }
设置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); }
设置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" />
实例化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>
添加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>
实例化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; }
- 实例化ConsoleAppender和PatternLayoutEncoder
- 获取logging.pattern.console的配置,如果获取不到则使用CONSOLE_LOG_PATTERN,然后设置到ConsoleAppender 输出格式中
- 启动ConsoleAppender
- 设置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>
如果logFile 不等于null
则创建fileAppender,添加到rootLogger中.相当于如下配置:
<root level="INFO"> <appender-ref ref="CONSOLE" /> <appender-ref ref="FILE" /> </root>
否则只添加consoleAppender到rootLogger中.相当于如下配置:
<root level="INFO"> <appender-ref ref="CONSOLE" /> </root>
ColorConverter
该类用来处理颜色
字段如下:
// 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); }
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); }
- 通过elements根据Option 获得AnsiElement
- 如果没有对应的Element,则根据日志级别获得,如果还是获取不到,则返回GREEN
转成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
- 由于 2018-01-22 16:38:41.812 属于默认配置的日志格式中的%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} 因此会在transform方法的第1步获得AnsiStyle.FAINT
- 由于 INFO 属于默认配置的日志格式中的%clr(${LOG_LEVEL_PATTERN:-%5p}) 由于没有配置颜色,同时在ColorConverter中也没有配置info级别所对应的颜色,因此返回的是AnsiColor.GREEN
- 由于 55134 属于默认配置的日志格式中的%clr(${PID:- }){magenta} 因此会在transform方法返回AnsiColor.MAGENTA
- 由于 — 属于默认配置的日志格式中的%clr(—){faint} 因此会在transform方法返回AnsiStyle.FAIN
- 由于 [ main] 属于默认配置的日志格式中的%clr([%15.15t]){faint} 因此会在transform方法返回AnsiStyle.FAIN
- 由于 com.example.demo.DemoApplication 属于默认配置的日志格式中的%clr(%-40.40logger{39}){cyan} 因此会在transform方法返回AnsiColor.CYAN
- 由于 : 属于默认配置的日志格式中的%clr(:){faint} 因此会在transform方法返回AnsiStyle.FAIN
ExtendedWhitespaceThrowableProxyConverter
该类是在异常堆栈的打印过程中添加一些空格.
throwableProxyToString–> 在打印日志的过程中会调用.代码如下:
protected String throwableProxyToString(IThrowableProxy tp) { return CoreConstants.LINE_SEPARATOR + super.throwableProxyToString(tp) + CoreConstants.LINE_SEPARATOR; }
其实就是在堆栈的日志前后加上换行符