LoggingApplicationListener 的执行
LoggingApplicationListener 的主要作用是配置LoggingSystem, 如果 环境 包含 loggingconfig 属性,LoggingApplicationListener 将用于引导 日志记录系统,否则使用默认配置。
如果环境包含 logging.level.*和日志记录组,则可以使用 logging.group 定义日志记录级别。
关于 LoggingApplicationL istener 的重点功能我们后面章节再进行讲解。
LoggingApplicationListener 实现自 GenericApplicationListener 接口,具有监听器的特性。
因此,执行 EventPublishingRunListener 广播事件之后,LoggingApplicationListener 便会监听到对应的事件并执行 onApplicationEvent 方法中的逻辑判断,有针对性地处理不同的事件,相关代码如下。
代码语言:javascript复制@Override
public void onApplicationEvent(ApplicationEvent event) {
if (event instanceof ApplicationStartingEvent) {
// springboot 启动时触发
onApplicationStartingEvent( (ApplicationStartingEvent) event);
} else if (event instanceof ApplicationEnvironmentPreparedEvent) {
// Environment 环境准备初级阶段触发
onApplicationEnvironmentPreparedEvent( (Applicat ionEnvironmentPrepared-
Event) event);
} else if (event instanceof ApplicationPreparedEvent) {//应用上下文准备完成,但未刷新时触发
onApplicationPreparedEvent( (Applicat ionPreparedEvent) event);
else if (event instanceof ContextClosedEvent
&& ((ContextClosedEvent) event). getApplicationContext() . getPar
ent() == null) {
//容器关闭时处理
onContextClosedEvent( );
} else if (event instanceof ApplicationFailedEvent) {
//启动失败时处理
onApplicationFailedEvent();
}
}
以上代码中的事件处理基本涵盖了 Spring Boot 启动的不同阶段和不同状况,比如 SpringBoot 刚刚启动阶段、环境准备初级阶段、应用上下文准备完成阶段、容器关闭阶段、应用程序启动失败等。后面章节我们会对这些过程中日志系统是如何处理的进行详解介绍。
ApplicationStartingEvent 事件处理
在 Spring Boot 的启动过程中,通过 SpringApplicationRunListeners 类间接的调用了EventPublishingRunListener 中 的 各 类 事 件 的 发 布 方 法 , 最 终 被 LoggingApplicationListener 监听并进行处理。在后续的讲解中,我们省略这个中间调用过程,直接讲解 Logging-ApplicationL istener 接收到事件后的处理。
Spring Boot 刚 刚 启 动 时 发 布 了 ApplicationStartingEvent 事 件 , LoggingApplication-Listener 中的 onApplicationStartingEvent 方法便被调用了,该方法源码如下。
代码语言:javascript复制private void onApplicat ionStartingEvent(ApplicationStartingEvent event)
this .loggingSystem = LoggingSystem. get(event . getSpringApplication(). getCl
assLoader()) ;
this. loggingSystem. beforeInitialize();
}
在 onApplicationStartingEvent 方法中,首先获得一个 LoggingSystem 对象,然后调用对象的 beforelnitialize 方 法进行预初始化操作。也就是说在 Spring Boot 开始启动时,日志系统做了两件事:创建 LoggingSystem 对象和预初始化操作。
LoggingSystem 为日志系统的通用抽象类,其中也提供了获取 LoggingSystem 对象的静态方法。上面 LoggingSystem 的创建便 是调用其 get 方法获得,相关代码如下。
代码语言:javascript复制public static LoggingSystem get(ClassLoader classLoader) {
//从系统变量中获得 L oggingSystem 的类名String loggingSystem = System. getProperty(SYSTEM PROPERTY);
f (StringUtils . hasLength(loggingSystem)) {
if (NONE . equals(loggingSystem))
return new NoOpLoggingSystem();
/如果存在,则通过反射进行对象的初始化
return get(classLoader, loggingSystem);
// MSYSTEMS 筛选并初始化 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"));
}
该方法首先判断系统中是否配置了 LoggingSystem 的配置,存在且不为“none”时,则利用反射机制进行初始化;如果明确配置为"none”,则返回 NoOpL oggingSystem 对象。实例化配置的 L oggingSystem 相关代码如下。
代码语言:javascript复制private static LoggingSystem get(ClassLoader classLoader, String loggingSys
temClass) {
try
Class<?> systemClass = ClassUtils . forName( loggingSystemClass, classLoad
er);
Constructor<?> constructor = systemClass . getDeclaredConstructor(Class-
Loader.
class);
constructor . setAccessible(true);
return (LoggingSystem) constructor. newInstance(classLoader);
} catch (Exception ex) {
throw new IllegalStateException(ex);
}
}
以上代码就是通过获取指定类的构造器,调用其 newInstance 方法来创建 LoggingSystem对象的。
如果系统中不存在该对象的配置,则从 SYSTEMS 筛选获取第一个符合条件的值,然后进行初始化。SYSTEMS 为 L oggingSystem 的静态变量,通过静态代码块进行初始化,相关代码如下。
代码语言:javascript复制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. LogbackLoggingSyste
m");
systems . put("org. apache. logging. log4j . core. impl. Log4jContextFactory"
"org. springframework. boot. logging . log4j2 . Log4J2LoggingSyste
m");
systems . put("java .util. logging. LogManager" ,
"org. springframework . boot . logging . java . Javal oggingSystem");
SYSTEMS = Collections . unmodifiableMap(systems);
}
常 量 SYSTEMS 是 Map 结 构 , 其 中 key 为 对 应 日 志 系 统 的 核 心 类 ( 类 似@ConditionalOn-Class 注解中指定的类), value 的值 是 LoggingSystem 的具体实现类。
在静态代码块中,初始化分别添加了 LogbackL oggingSystem、Log4J2L oggingSystem 和JavaLoggingSystem,这也是 Spring Boot 默认内置的 3 个日志实现类。而且 SYSTEMS 被初始化之后便不可被修改了。
其中从 SYSTEMS 中筛选出符合条件的 L oggingSystem 实现类,这里采用了 Java 8 新增的 Stream 语法来实现,基本处理过程是这样的:遍历 SYSTEMS 中的值,通过 ClassUtils的 isPresent 方 法过滤符合条件的值(key 对应的类存在于 classpath 中) ;然后通过上面提到的反射方法创建筛选过后的值的对象;最后获取第一个对象并返回。如果未获取到则抛出异常。
由于 SYSTEMS 是基于 LinkedHashMap 实现的,因此,这里可以看出默认情下 SpringBoot优先采用 org.springframework.bot.logging.logback.LogbackL oggingSystem 实现类。
也就是说,默认情况下使用 L ogback 进行日志配置。
完成 LoggingSystem 初始化之后,程序便调用其 beforelnitialize 方法进行初始化前的准备工作。在 L oggingSystem 中 beforelnitialize 为抽象方法,由子类实现。该方法在 LogbackLoggingSystem 中的源码实现如下。
代码语言:javascript复制public void beforeInitialize() {
//获得 LoggerContext
LoggerContext loggerContext = getLoggerContext();
//如果 LoggerContext 中已经配置有 oggingSystem 对应的 Logger,则直接返回
if (isAlreadyInitialized(loggerContext)) {
return;
11 调用父类的初始化方法
super . beforeInitialize();//向 LoggerContext 中的 TurboFilterL ist 添加 1 个 TurboFilter
// 目的是在 L oggerContext 没有初始化之前对应打印的日志的请求全部拒绝
loggerContext . getTurboFilterList() . add(FILTER);
}
该方法的主要功能是获得 LoggerContext 并校验是否存在对应的 logger,如果不存在则调用父类的初始化方法,并拒绝在 L oggerContext 没有初始化之前对应打印的日志的全部请求。
LogbackL oggingSystem 的 父 类 为 SIf4JL oggingSystem, 因 此 方 法 中 调 用 了 SIf4JLogging-System 的 beforelnitialize 方法,相关源码如下。
代码语言:javascript复制public abstract class Slf4JLoggingSystem extends AbstractLoggingSystem {
private static final String BRIDGE_ HANDLER = "org. slf4j . bridge . SLF4JBridg
eHandler";
@Override
publi
ic void beforeInitialize()
//调用父类的 beforeInitialize, 默认父类实现为空
super . beforeInitialize();
//配置 jdk 内置日志与 SLF4J 直接的娇接 Handler
configureJdkL oggingBridgeHandler();
private void configureJdkl oggingBridgeHandler() {
//判断是否需要将 JUL 桥接为 SLf4j
if (isBridgeJulIntoSlf4j()) {
//删除 jdk 内置日志的 Handler
removeJdk oggingBridgeHandler();
//添加 SLF4J 的 Handler
SLF4JBridgeHandler . install();
catch (Throwable ex) {
//忽略异常。没有 java. util. Logging 桥接被安装
}
//根据两个条件判断是否将 JUL 桥接为 SLF4J
protected final boolean isBridgeJulIntoSlf4j() {
return isBridgeHandlerAvailable() && isJulUsingASingleConsoleHandler-
AtMost();
/判断 org. slf4j. bridge. SLF4JBridgeHandler 是否存在于类路径下
protected final boolean isBridgeHandlerAvailable()
return ClassUtils. isPresent(BRIDGE_ HANDLER, getClassLoader());//判断是否不存在 HandLer 或只存在-个 Consol eHandler
private boolean isJulUsingASingleConsoleHandlerAtMost() {
Logger rootLogger = LogManager . getLogManager(). getLogger("");
Handler[] handlers = rootlogger. getHandlers();
return handlers.length == 0 | | (handlers.length == 1 && handlers[0] ins
tanceof-
ConsoleHandler);
//移除 Handler
private void removeJdkL oggingBridgeHandler() {
try {
removeDefaultRootHandler();
//移除 SLF4J 相关 Handler
SLF4JBridgeHandler . uninstall();
} catch (Throwable ex) {
//忽略并继续
//移除 ConsoleHandler
private void removeDefaultRootHandler() {
try {
Logger rootLogger = LogManager . getLogManager().getLogger(" );
Handler[] handlers = rootl ogger. getHandlers( );
if (handlers.length == 1 && handlers[0] instanceof ConsoleHandler) {
rootLogger . removeHandler(handlers[0]);
} catch (Throwable ex) {
//忽略并继续
}
}
}
上述代码涵盖了 SIf4JL oggingSystem 中大多数的功能,其主要目的就是处理内置日志(JUL)与 SLF4J 的 Handler 的桥接转换操作。
基本判断逻辑如下:如果类路径下存在 SLF4JBridgeHandler 类,并且根 Logger 中不包含或仅包含 ConsoleHandler 时,说明需要将内置日志转换为 SLF4J。
基本转换过程分两步:删除原有 Handler、新增指定的 Handler; 如果满足条件,先删除内置日志的Handler,然后再删除SLF4J的Handler,最后再将SLF4J对应的SLF4JBridgeHandler添加到根 L ogger 中。
具体的方法实现参考上述代码中的注解,关于 SLF4J 的 uninstall 方法和 install 方法均在SLF4JBridgeHandler 类中,实现比较简单,在此不再进行拓展。不过建议读者朋友阅读一下 SLF4JBridgeHandler 中的源代码, 其内部还提供了转换过程中各层级日志级别对应等处理。
至此,针对 Spring Boot 启动阶段发出的 ApplicationStartingEvent 事件及日志系统所做的相应操作已经讲解完毕。
ApplicationEnvironmentPreparedEvent 事件处理
当 SpringBoot 继续启动操作便会广播 ApplicationEnvironmentPreparedEvent 事件,此时便会调用 LoggingApplicationListener 的 onApplicationEnvironmentPreparedEvent 方法,该方法源码如下。
代码语言:javascript复制private void onApplicationEnvironmentPreparedEvent(
ApplicationEnvironmentPreparedEvent event) {
if (this.loggingSystem == null)
this. loggingSystem = LoggingSystem
.get(event
getSpringApplication()
initialize(event. getEnvironment(), event . getSpringApplication(). getClassL
oader());
}
在上一节中,ApplicationStartingEvent 事件触发时 ,loggingSystem 已经初始化赋值了,在该方法中会再次判断loggingSystem是否为null,如果为null,则通过LoggingSystem的 get方法进行对象创建。
完成 loggingSystem 的再次判断并创建之后,调用 initialize 方法进行初始化操作,主要完成了初始参数的设置、日志文件、日志级别设置以及注册 ShutdownHook 等操作,相关代码如下。
代码语言:javascript复制protected void initialize(ConfigurableEnvironment environment, ClassLoader
classLoader) {
//创建 LoggingSystemProperties 对象,并 没置默认属性
new LoggingSystemProperties( environment) . apply();
//获取 LogFile,如果 LogFile 存在, 则向系统属性写入 LogFile 配置的文件路径
this.logFile = LogFile . get( environment);
if (this.logFile != null) {
this . logFile . applyToSystemProperties();
this. loggerGroups = new LoggerGroups(DEFAULT_ GROUP_ LOGGERS);
!1 rsrinooogging
initializeEarlyLoggingL evel(environment);
//初始化 L oggingSysteminitializeSystem(environment, this . loggingSystem, this. logFile);
//最终没置日志級别
initializeFinaloggingL evels(environment, this . loggingSystem) ;
//注册 ShutdownHook
registerShutdownHookIfNecessary(environment, this . loggingSystem);
}
上述代码中,创建 LoggingSystemProperties 对象之 后主要是通过调用其 apply 方法来获取默认的日志配置参数(在配置文件中以"logging.”开头的属性),并设置到系统属性中。
LogFile的get方法主要是获取日志文件的路径和名称,并作为参数创建Logfile对象。LogFile中 get 方法相关代码如下。
代码语言:javascript复制public static LogFile get(PropertyResolver propertyResolver) {
Strinp file = petI OpFi ] ePronertv( nronertvResolver. FTI F NAMF PROPERTY. F T
LE_ PROPERTY);
String path = getLogFileProperty(propertyResolver, FILE_ PATH PROPERTY, PA
if (StringUtils.haslength(file) | StringUtils.haslength(path)) {
return new LogFile(file, path);
}
return null;
}
从上述代码可以看出,通过获取属性名为"logging.ile.path"的值得到了 日志的路径,通过获取属性名为"logging.file.name "的值得到了日志文件名。其中 getLogFileProperty 的第 3 个参数为兼容历史版本中的配置属性名。当然,程序会优先获取当前版本的属性配置,当查找不到值时才会获取历史版本的值。
紧接着,initialize 方法中判断当 LogFile 不为 null 时,调用它的 apply ToSystemProperties方法,也就是将上述获得的日志文件路径和名称存入系统属性当中。
initializeEarlyL oggingl .evel 方法用于早期设置 springBootl ogging 的值和 LoggingSystem的初始化,代码如下。
代码语言:javascript复制private void initializeEarlyLoggingl evel (Conf igurableEnvironment environmen
if (this. parseArgs && this . springBootLogging == null) {
f (isSet(environment, "debug")) {
this . springBootLogging = LogLevel. DEBUG;
this. springBootlogging = Loglevel. TRACE;
}
}
}
上述代码主要根据 parseArgs 参数(默认为 true )和 springBootLogging 是否为 null,在早期阶段中设置 springBootL ogging 的值,也就是日志级别。
在 parseArgs 为 true, 并 且 springBootLogging 值 为 null 的 情 况 下 , 如 果Configurable-Environment 中 debug 的值存在且为 true, 则设置 springBootL ogging 为DEBUG。同样,如果 trace 的值存在且为 true,则设置 springBootLogging 为 TRACE。
初始化 LoggingSystem 的代码如下。
代码语言:javascript复制private void initializeSystem(Conf igurableEnvironment environment,
LoggingSystem system, LogFile logFile) {
//实例化 LoggingInitial izat ionContext
LoggingInitializationContext initializationContext = new LoggingInitializ
ationContext (
environment);
String 1ogConfig 二 enviroment .getProperty(CONFITG PROPERTY);
if (ignoreLogConfig(logConfig))
//如果 logging. config 没有配置或者配置的值是 D 开头的,则调用 L oggingSystem
的方法进行初始化
system . initialize(initializationContext, null, logFile);
} else {
11 通过 ResourceUtils 进行加载判断其文件是否存在,如果不存在,则抛出 Illega
lStateException
ResourceUtils .getURL(logConfig) .openStream().close();
//存在则调用 L oggingSystem 的方法进行实例化
system. initialize(initializationContext, logConfig, logFile);
} catch (Exception ex) {
private boolean ignoreLogConfig(String logConfig) {
return !StringUtils.hasLength(logConfig) 11 logConfig. startsWith("-D");
}
initializeSystem 方法中代码的逻辑主要是围绕 L oggingSystem 的初始化来进行的,首先为其初始化准备了 LogginghitializationContext 对象。然后获取 logging.config 的参数并赋值给 logConfig,如果 logConfig 未配置 或者配置的值以 D 开头,则调用 LoggingSystem 的initialize 方法进行初始化; 其他情况则通过 ResourceUtil 加载判断对应配置文件是否存在,如果不存在,则抛出llegalStateException;如果存在,则同样调用L oggingSystem的initialize方法进行初始化。
下面我们来看 LoggingSystem 的 itialize 方法的源代码。该方法在 LoggingSystem 类中的实现为空,而在其子类 AbstractL oggingSystem 中提供了如下的实现。
代码语言:javascript复制@Overridepublic void initialize(LoggingInitializationContext initializationContext,
String configLocation, LogFile logFile) {
if (StringUtils . hasLength(configLocation))
initializeWi thSpecificConfig(initializationContext, configLocation, log
File);
return;
}
initializeWithConvent ions (initializationContext, logFile);
}
.上述代码中,如果用户指定了配置文件,则加载指定配置文件中的属性进行初始化操作;如果未指定配置,则加载默认的配置,比如 log4j2 的 log4j2 .properties 或 log4j2.xml。其中默认加载日志配置文件名称及文件格式由具体的子类实现。
下面重点讲解 SpringBoot 中默认查找配置文件路径的实现,该部分在 LoggingSystem的抽象子类 AbstractL oggingSystem 的 initializeWithConventions 中实现。
代码语言:javascript复制private void initializeWithConventions(
LoggingInitializationContext initializationContext, LogFile logFile)
String config = getSelfInitializat ionConfig();
if (config != null && logFile == null) {
//自我初始化操作,属性变更时会重新初始化
reinitialize(initializationContext);
return;
}
if (config == null) {
config = getSpringInitializationConfig();
}
if (config != null) {
loadConfiguration(initializationContext, config, logFile);
return;
loadDefaults(initializationContext, logFile);
}
该方法的基本流程是:首先,获得默认的日志配置文件(比如 logback.xml 等), 当配置文件不为 null,且 logFile 为 null 时, 进行自我初始化,具体实现由不同的日志框架来执行,主要就是重置数据并加载初始化;然后,如果默认的配置文件不存在,则尝试获取包含“-spring”的名称的配置文件(比如 logback-spring.xmI 等),如果获得对应的配置文件,则直接加载初始化;最后,如果上述两种类型的配置文件均未找到,则调用 loadDefaults 方法采用默认值进行加载配置。
getSelflnitializationConfig 方法和 getSpringlnitializationConfig 都是用来获取默认配置文件名称的,不同之处在于获得的配置文件的名称中是否多了“一 spring"。这两个方法都是先调用 getStandardConfig ocations 方法获得默认的配置文件名称数组,然后再调用 findConfig来验证获取符合条件的值。
我们先看不同之处, getSpringhnitializationConfig 方法通过 getSpringConfigL ocations 来获得配置文件名称数组。
代码语言:javascript复制protected String[] getSpringConfigLocations() {
//获得默认配置文件的路径
String[] locations = getStandardConfigLocations();
for (int i = 0; i < locations.length; i ) {
String extension = StringUtils. getFilenameExtens ion(locations[i]);
locations[i] = locations[i]. substring(0,
locations[i].length() - extensio
n.1ength() - 1) "-spring.
t extension;
return locations;
}
上述代码中,通过 getStandardConfigLocations 获得 了默认配置文件名称数组,然后对路径中的文件名进行兼容处理,比如默认配置文件名称为 logback.xml ,当我们配置为logback-spring.xml 时 , 通 过 getSelfInitializationConfig 方 法 无 法 加 载 到 , 但 通 过getStandard-ConfigLocations 方法则可以加载到。上述方 法核心处理就是将默认的配置文件名截取之后拼接上了“-spring"。
其中两个方法都调用了 getStandardConfigLocations 方法为 AbstractLoggingSystem 的抽象方法,具体实现由具体日志框架的子类来完成,比如在 L ogbackL oggingSystem 中,该方法的实现如下。
代码语言:javascript复制protected String[] getStandardConfigLocations() {
return new String[] { "logback- test . groovy", "logback-test . xml", "logbac
k. groovy"
"logback.xml" };
}
也就是说,LogbackLoggingSystem 默认支持以 logback-test.groovy、logback-test.xml、logback.groovy、logback.xm 以及 上述名称扩展了“-spring”(比如 logback-spring.xml)的配置文件。
无论是通过配置指定配置文件名称,还是通过上述默认方式获得配置文件名称,当获得之后,都会调用 loadConfiguration 方法进行配置的加载。loadConfiguration 方法由子类来实现,比如,LogbackLoggingSystem 中实现的源码如下。
代码语言:javascript复制@Override
protected void loadConfiguration(LoggingInitializat ionContext initializat
String location, LogFile logFile) {
super . loadConfiguration(initializationContext, location, logFile);
//获得 LoggerContext, 这里实际上是 ILoggerFac tory 的具体实现
LoggerContext loggerContext = getLoggerContext();
stopAndReset(loggerContext);
configureByResourceUrl(initializationContext, loggerContext,
} catch (Exception ex) {
ResourceUtils.getURL(location));
}
}
}
上述代码中首先会调用父类的 loadConfiguration 方法,该方法的最终操作还是调用了前面讲到的 LoggingSystemProperties#apply 方法进行参数的获取,并设置到系统属性中。
getL oggerContext 获得了 LoggerContext 对象,本质上 LoggerContext 是 ILoggerFactory的具体实现类。随后通过 stopAndReset 方法对日志相关的参数、监听器等进行停止和重置。
configureByResourceUrl 方法重点实现了针对 xml 格式的配置文件和其他格式(比如 groovy后缀)的配置文件的解析和具体配置,相关操作由对应的日志框架内部提供的类来实现。
最 后 , 再 回 到 AbstractL oggingSystemtinitializeWithConventions 方 法 中 调 用 的IloadDefaults 方法,看看当未查找到配置文件时是如何处理的。
loadDefaults 方法同样是抽象方法,在 LogbackLoggingSystem 中的具体实现如下。
代码语言:javascript复制@Override
protected void loadDefaults(LoggingInitializationContext initializationCo
ntext, LogFile
//获得 L oggerContext(ILoggerFactory)并进行重置操作
secrerecntxt BetLossercontext();
//获得是否为 debug 模式
boolean debug = Boolean.getBoolean("logback. debug");
//如果为 debug 模式则添加控制台监听器
StatusL istenerConfi gHelper . addOnConsoleL istenerInstance(context, new On
Statuslistener
//根据是否为 debug 模式创建不同的 L ogbackConfigurator
LogbackConfigurator configurator = debug ? new Debugl ogbackConfigurator(c
: new LogbackConfigurator(context);i 配置白志级别将( LEVEL PATTERNE 默认 5setnmironomnti
context . putProperty(LoggingSystemProperties.L0G_ LEVEL_ PATTERN,
environment . resolvePlaceholders("${logging. pattern.le
vel:${LOG_ LEVEL_
PATTERN:%5p}}"));
//配置日志中时间格式
(LOG DATEFORMAT_ PATTERN), 默认为 y>y -M-dd HH: m:ss.sssS
oggingSystemProperties.
LOG
DATEFORMAT_
PATTERN,
environment.
resolvePlacehold
0BB1
"${logging . pattern.
dateformat:${LOG_ DATEFORMAT_ PATTERN:yyy-MM-dd HH:
m.SS}"));
//配置文件名称格式
(ROLLING FILE_ _NAME_ PATTERN),默认为${LOG _FILE}. %d{yyy -MM-dd}. %i.gz}
(logingSystemProperties . RLLING FILE NANE PATTERN, environment
.resolvePlaceholders("${logging. pattern. rolling-file-name:${LOG_ FILE} .
%yy--d}- .%i.g}));
new DefaultLogbackConfiguration(initializationContext, logFile). apply(confi
gurator);
context . setPackagingDataEnabled(true);
}
以 上 代 码 主 要 进 行 了 LoggerContext 重 置 、 日志输出格 式 、 日志文 件 名 、Logback-Configurator (编程风格配置 logback)等设置,具体操作的作用可对照上述代码中的注解进行了解。
回到最初 L oggingApplicationListener 的 initializeSystem 方法,值得注意的是,在通常情况下,该方法中往往不是直接采用 LoggingSystem 的抽象类 AbstractL oggingSystem 中的initialize 方法实现,而是通过不同日志框架重新实现,在恰当的时机会调用 AbstractL oggingSystem 的 initialize 方法。这样做的好处是可以根据不同的日志框架进行定制化的扩展。比如LogbackL oggingSystem 中 initialize 方法的实现如下。
代码语言:javascript复制@Override
public void initialize(LoggingInitializationContext initializationContex
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(LogbackL oggingSystem. class . getName()) .warn(
"Ignoring '” CONFIGURATION_ FILE_ PROPERTY ”' system property.'
"Please use ' logging. config' instead.");
}
}
我们可以看到,在 LogbackL oggingSystem 的实现类中不仅在调用父类的 initialize 方法之前进行了是否已经初始化的判断,还在调用父类 initialize 方法之后,实现了它自己的一些业务逻辑,比如移除 LoggerContext 的 TurboFilterList 中添加的 TurboFilter、标记初始化状态。
在完成了以上步骤之后,日志系统已经正式启动,可以进行正常的日志输至此,针 对 LoggingApplicationListener 中 ApplicationEnvironmentPreparedEvent 事件的处理已经讲解完毕。
小结
本章详细介绍了 Spring Boot 启动过程中日志事件的触发,以及事件发布之后,日志系统所对应的处理。在 LoggingApplicationListener 的 onApplicationEvent 方法中还有其他事件的处理,比如:
ApplicationPreparedEvent、ContextClosedEvent 、ApplicationFailedEvent 等,但相对于上述过程,这些事件的日志处理比较简单,读者可自行阅读。当然,如果对日志系统感兴趣,可针对具体的技术框架进行更加深入地学习。
本文给大家讲解的内容是Spring Boot日志源码解析:LoggingApplicationListener的执行
- 下篇文章给大家讲解的是创建SpringBoot自动配置项目;
- 觉得文章不错的朋友可以转发此文关注小编;
- 感谢大家的支持!
本文就是愿天堂没有BUG给大家分享的内容,大家有收获的话可以分享下,想学习更多的话可以到微信公众号里找我,我等你哦。