一、简述
SOFABoot提供了日志空间隔离能力。
SOFABoot中间件能自动发现SOFABoot应用的日志实现依赖,并独立打印日志,解耦SOFABoot中间件日志和SOFABoot应用日志。
SOFABoot通过其sofa-common-tools包提供日志空间隔离功能。
首先看一下sofa-common-tools包中与日志空间隔离功能相关类的类图:
通过上述类图可以看出,其抽象日志空间工厂类AbstractLoggerSpaceFactory实现了org.slf4j.ILoggerFactory接口,所以,SOFABoot基于SLF4J(Simple logging Facade forJava)日志组件实现日志空间隔离功能。对于SLF4J,它不同于其他日志类库,不是一个真正的日志实现,而是一个抽象层,它允许你在后台使用任意一个日志类库。相信大多数Java开发人员都使用过,在此不详述。
SOFABoot日志空间隔离功能实现的基本原理如下:
SOFABoot通过各种日志空间工厂Builder,根据SOFABoot中间件依赖的日志类库(SOFA支持的日志类库:logback、log4j2、log4j、commons-logging),为每个SOFABoot中间件创建各自的日志空间工厂类(抽象类AbstractLoggerSpaceFactory的匿名实现类)。在每个日志空间工厂类中,每个SOFABoot中间件及SOFABoot应用拥有其独有的日志空间(logback和log4j2使用ch.qos.logback.classic.LoggerContext类,log4j和commons-logging使用org.apache.log4j.spi.LoggerRepository实现类),并使用日志空间管理该中间件使用的所有logger,从而实现各SOFABoot中间件之间,以及其与SOFABoot应用之间的日志空间隔离,彼此独立配置,独立管理,互不影响。
总结一下,主要包括两个方面的内容:
1. 根据classpath路径上所包含的日志类库Jar包,自动选择所使用的日志类库;
2. 根据所选的日志类库,为SOFABoot 应用和中间件创建独立的日志空间,管理各自的logger对象;
二、使用方式
(一)编写日志配置文件
针对每种日志类库,SOFABoot中间件需要提供相应的日志配置文件。
分别编写logback、log4j2、log4j格式的日志配置文件log-conf.xml,然后按照日志类库的别名,分别存储在项目资源主目录src/main/resources下不同子目录中。
子目录创建规则如下:
子目录格式:中间件日志空间名称转换路径/log/不同日志类库的名字。
其中:
1. 中间件日志空间名称转换路径为把中间件日志空间名称中的.转换为/。例如:com.alipay.sofa.infra转换为路径com/alipay/sofa/infra。
2. 不同日志类库的名字分别为logback、log4j2、log4j。
在Infra模块中,日志配置文件的存储路径依次为:
/com/alipay/sofa/infra/log/logback/log-conf.xml;
/com/alipay/sofa/infra/log/log4j2/log-conf.xml;
/com/alipay/sofa/infra/log/log4j /log-conf.xml;
(二)编写一个Logger工厂类
此类主要功能是从spaceName的空间里寻找指定名字的logger对象,这些 logger是从该spaceName下的日志实现配置中解析而来。
例如:在SOFABoot中,Infra模块com.alipay.sofa.infra.log.InfraHealthCheckLoggerFactory,Healthcheck模块com.alipay.sofa.healthcheck.log.SofaBootHealthCheckLoggerFactory,Runtime模块的com.alipay.sofa.runtime.spi.log.SofaRuntimeLoggerFactory。
以InfraHealthCheckLoggerFactory类为例,详细说明基本实现方式(如果没有其它特殊需求,除了红色代码需要根据具体SOFABoot中间件的日志空间名字修改外,其它保持不变):
1. public class InfraHealthCheckLoggerFactory{
2.
3. public static final StringINFRASTRUCTURE_LOG_SPACE = "com.alipay.sofa.infra";
4.
5. /***
6. * 获取日志对象
7. *
8. * @param clazz 日志的名字
9. * @return 日志实现
10. */
11. public static org.slf4j.LoggergetLogger(Class<?> clazz) {
12. if (clazz == null) {
13. return null;
14. }
15. returngetLogger(clazz.getCanonicalName());
16. }
17.
18. /**
19. * 获取日志对象
20. *
21. * @param name 日志的名字
22. * @return 日志实现
23. */
24. public static org.slf4j.LoggergetLogger(String name) {
25. if (name == null || name.isEmpty()) {
26. return null;
27. }
28. returnLoggerSpaceManager.getLoggerBySpace(name, INFRASTRUCTURE_LOG_SPACE);
29. }
30. }
设置Infra模块的日志空间名称。一般为含模块名称的包路径,此处为:com.alipay.sofa.infra。
调用LoggerSpaceManager类getLoggerBySpace方法,从spaceName的空间里寻找指定名字的logger对象。
(三)使用logger记录日志信息
当我们需要在SOFABoot中间件的某个类中记录日志时,直接使用该中间件的Logger工厂类获取其日志空间中指定logger名称的logger对象,然后像使用SLF4J的logger一样,记录日志信息。
例如,在Infra模块SofaBootVersionEndpoint类中,代码如下:
1. private static final Logger logger =InfraHealthCheckLoggerFactory
2. .getLogger(SofaBootVersionEndpoint.class);
三、源码解析
以SOFABoot研发框架的基础模块infra-sofa-boot-starter为例,详细描述日志空间隔离功能的实现原理。
在此提前说明,源码分析主要分析主流程,以及本人认为比较重要的一些内容,对于其它部分,大家可以基于本文档,自行研读。
还是以SOFABoot自带的sofa-boot-sample为例,它依赖于infra-sofa-boot-starter模块。
与SpringBoot最主要的特性自动配置AutoConfig一样,SOFABoot也是通过提供infra-sofa-boot-starter为我们自动配置。
我们只需要在项目sofa-boot-sample的pom.xml文件中,添加infra-sofa-boot-starter到项目依赖中即可。
1. <dependency>
2. <groupId>com.alipay.sofa</groupId>
3. <artifactId>infra-sofa-boot-starter</artifactId>
4. </dependency>
而infra-sofa-boot-starter依赖于sofa-common-tools包。
1. <dependency>
2. <groupId>com.alipay.sofa.common</groupId>
3. <artifactId>sofa-common-tools</artifactId>
4. </dependency>
通过SpringApplication类run方法,启动项目:
1. @SpringBootApplication
2. public class DemoApplication {
3. public static void main(String[] args) {
4. SpringApplication.run(DemoApplication.class,args);
5. }
6. }
本文主要详述日志空间隔离相关的内容,SOFABoot的启动原理可以参考《启动原理》,在此不在详述。
由于SOFABoot 基于SpringBoot进行构建,所以完全兼容SpringBoot。作为一个遵循SpringBoot规范的Jar包,也是通过自动配置(spring.factories)和SOFABootInfrastructureSpringContextInitializer(实现了ApplicationContextInitializer接口)完成infra的初始化工作。
infra-sofa-boot-starter模块的spring.factories文件内容如下:
1. # Initializers
2. org.springframework.context.ApplicationContextInitializer=\
3. com.alipay.sofa.infra.initializer.SOFABootInfrastructureSpringContextInitializer
4.
5. # Auto Configure
6. org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
7. com.alipay.sofa.infra.autoconfigure.SofaBootInfraAutoConfiguration
在SpringApplication实例初始化的时候,使用SpringFactoriesLoader在应用的classpath中查找并加载所有可用的ApplicationContextInitializer接口实现类,此时会加载SOFABootInfrastructureSpringContextInitializer。
SpringApplication实例初始化完成后,开始执行run方法的逻辑。
在调用prepareContext方法准备Spring应用上下文过程中,依次调用刚才加载的ApplicationContextInitializer接口的实现类的initialize方法,完成Spring应用上下文的初始化工作。此时,会调用SOFABootInfrastructureSpringContextInitializer的initialize方法,完成Infra相关的初始化工作:
1. public voidinitialize(ConfigurableApplicationContext applicationContext) {
2. //init log
3. this.logInit(applicationContext.getEnvironment());
4.
5. InfraHealthCheckLoggerFactory.getLogger(
6. SOFABootInfrastructureSpringContextInitializer.class).info(
7. "SOFABoot InfrastructureStarting!");
8. }
首先,调用logInit方法,根据application.properties文件中以logging为前缀的日志相关属性设置系统属性,如logging.path、logging.level等。
然后,以SOFABootInfrastructureSpringContextInitializer类的class对象为参数,调用InfraHealthCheckLoggerFactory类getLogger方法:
1. public static org.slf4j.LoggergetLogger(Class<?> clazz) {
2. if (clazz == null) {
3. return null;
4. }
5. returngetLogger(clazz.getCanonicalName());
6. }
以全限定类名作为参数,调用getLogger方法从Infra模块的日志空间中获取名为com.alipay.sofa.infra.initializer.SOFABootInfrastructureSpringContextInitializer的Logger实例:
1. public static org.slf4j.Logger getLogger(Stringname) {
2. //从"com/alipay/boot/healthcheck/log"中获取 health check 的日志配置并寻找对应logger对象,log 为默认添加的后缀
3. if (name == null || name.isEmpty()) {
4. return null;
5. }
6. return LoggerSpaceManager.getLoggerBySpace(name, INFRASTRUCTURE_LOG_SPACE);
7. }
调用LoggerSpaceManager类的getLoggerBySpace方法,获取指定日志空间中指定名字的Logger实例。此处,logger名称为com.alipay.sofa.infra.initializer.SOFABootInfrastructureSpringContextInitializer,日志空间名称为com.alipay.sofa.infra。
1. public static Logger getLoggerBySpace(Stringname, String spaceName) {
2. return getLoggerBySpace(name, newSpaceId(spaceName),
3. Collections.<String, String>emptyMap());
4. }
增加日志属性集合参数Map,调用getLoggerBySpace方法:
1. public static LoggergetLoggerBySpace(String name, SpaceId spaceId,
2. Map<String,String> properties) {
3. //init first
4. init(spaceId, properties);
5. returnMultiAppLoggerSpaceManager.getLoggerBySpace(name, spaceId);
6. }
初始化MultiAppLoggerSpaceManager实例,主要是创建日志空间SpaceInfo实例,设置其properties属性,最后缓存spacesMap,以便后续使用。
调用MultiAppLoggerSpaceManager类getLoggerBySpace方法:
1. public static LoggergetLoggerBySpace(String name, SpaceId spaceId) {
2. return getLoggerBySpace(name, spaceId,MultiAppLoggerSpaceManager.class.getClassLoader());
3. }
增加日志空间类加载器参数,调用getLoggerBySpace方法:
1. public static LoggergetLoggerBySpace(String name, SpaceId spaceId, ClassLoader spaceClassloader) {
2. AbstractLoggerSpaceFactoryabstractLoggerSpaceFactory = getILoggerFactoryBySpaceName(
3. spaceId, spaceClassloader);
4. return abstractLoggerSpaceFactory.getLogger(name);
5. }
(一)创建日志空间工厂类
调用getILoggerFactoryBySpaceName方法,根据中间件使用的日志类库创建日志空间工厂类:
1. private static AbstractLoggerSpaceFactorygetILoggerFactoryBySpaceName(SpaceId spaceId,
2. ClassLoaderspaceClassloader) {
3. //该判断,线程安全不是必须的(最多产生个TemporaryILoggerFactory实例,而且[未初始化]应该基本只发生在启动场景,此时也就基本就是单一线程),以便减少同步开销
4. if (!isSpaceInitialized(spaceId)) {
5. //temporary factory, and cachesupport
6. returnTemporaryILoggerFactoryPool.get(spaceId, spaceClassloader);
7. }
8.
9. AbstractLoggerSpaceFactoryiLoggerFactory = NOP_LOGGER_FACTORY;
10. if (!isSpaceILoggerFactoryExisted(spaceId)){
11. synchronized(MultiAppLoggerSpaceManager.class) {
12. if(!isSpaceILoggerFactoryExisted(spaceId)) {
13. iLoggerFactory = createILoggerFactory(spaceId,spaceClassloader);
14. spacesMap.get(spaceId).setAbstractLoggerSpaceFactory(iLoggerFactory);
15. }
16. }
17. } else {
18. iLoggerFactory =spacesMap.get(spaceId).getAbstractLoggerSpaceFactory();
19. }
20. return iLoggerFactory;
21. }
主要看一下createILoggerFactory方法:
1. private static AbstractLoggerSpaceFactorycreateILoggerFactory(SpaceId spaceId,
2. ClassLoaderspaceClassloader) {
3. if(System.getProperty(SOFA_MIDDLEWARE_LOG_DISABLE_PROP_KEY) != null
4. &&Boolean.TRUE.toString().equalsIgnoreCase(
5. System.getProperty(SOFA_MIDDLEWARE_LOG_DISABLE_PROP_KEY))){
6. ……略
7. return NOP_LOGGER_FACTORY;
8. }
9. // set log props
10. LogEnvUtils.processGlobalSystemLogProperties();
11.
12. // do create
13. try {
14. if(LogEnvUtils.isLogbackUsable(spaceClassloader)) {
15. String isLogbackDisable =System
16. .getProperty(LOGBACK_MIDDLEWARE_LOG_DISABLE_PROP_KEY);
17. if (isLogbackDisable != null
18. &&Boolean.TRUE.toString().equalsIgnoreCase(isLogbackDisable)) {
19. ……略
20. } else {
21. ……略
22. LoggerSpaceFactoryBuilderloggerSpaceFactory4LogbackBuilder = new LoggerSpaceFactory4LogbackBuilder(
23. spacesMap.get(spaceId));
24. returnloggerSpaceFactory4LogbackBuilder.build(spaceId.getSpaceName(),
25. spaceClassloader);
26. }
27. }
28.
29. if(LogEnvUtils.isLog4j2Usable(spaceClassloader)) {
30. String isLog4j2Disable = System.getProperty(LOG4J2_MIDDLEWARE_LOG_DISABLE_PROP_KEY);
31. if (isLog4j2Disable != null
32. &&Boolean.TRUE.toString().equalsIgnoreCase(isLog4j2Disable)) {
33. ……略
34. } else {
35. ……略
36. LoggerSpaceFactoryBuilderloggerSpaceFactory4Log4j2Builder = new LoggerSpaceFactory4Log4j2Builder(
37. spacesMap.get(spaceId));
38. return loggerSpaceFactory4Log4j2Builder.build(spaceId.getSpaceName(),
39. spaceClassloader);
40. }
41. }
42.
43. if(LogEnvUtils.isLog4jUsable(spaceClassloader)) {
44. String isLog4jDisable =System.getProperty(LOG4J_MIDDLEWARE_LOG_DISABLE_PROP_KEY);
45. if (isLog4jDisable != null
46. &&Boolean.TRUE.toString().equalsIgnoreCase(isLog4jDisable)) {
47. ……略
48. } else {
49. ……略
50. LoggerSpaceFactoryBuilderloggerSpaceFactory4Log4jBuilder = newLoggerSpaceFactory4Log4jBuilder(spacesMap.get(spaceId));
51. returnloggerSpaceFactory4Log4jBuilder.build(spaceId.getSpaceName(),
52. spaceClassloader);
53. }
54. }
55.
56. if(LogEnvUtils.isCommonsLoggingUsable(spaceClassloader)) {
57. //此种情形:commons-logging 桥接到 log4j 实现,默认日志实现仍然是 log4j
58. String isLog4jDisable = System
59. .getProperty(LOG4J_COMMONS_LOGGING_MIDDLEWARE_LOG_DISABLE_PROP_KEY);
60. if (isLog4jDisable != null
61. &&Boolean.TRUE.toString().equalsIgnoreCase(isLog4jDisable)) {
62. ……略
63. } else {
64. ……略
65. LoggerSpaceFactoryBuilderloggerSpaceFactory4Log4jBuilder = new LoggerSpaceFactory4CommonsLoggingBuilder(
66. spacesMap.get(spaceId));
67.
68. return loggerSpaceFactory4Log4jBuilder.build(spaceId.getSpaceName(),
69. spaceClassloader);
70. }
71. }
72. ……略
73. } catch (Throwable e) {
74. ……略
75. }
76. return NOP_LOGGER_FACTORY;
77. }
该方法判断sofa.middleware.log.disable属性是否为true。如果为true,则表示禁止中间件使用自己的日志空间,使用默认的NOP日志空间,NOP使用名为com.alipay.sofa.common.log的日志记录器logger。如果为false,创建新的日志空间工厂。
此处sofa.middleware.log.disable为false,所以开始创建新的日志空间工厂。
首先,在系统变量中设置全局日志相关的配置变量,如:file.encoding、logging.path、loggingRoot等。
然后,判断中间件或应用所使用的日志类库,创建相应的日志空间工厂。具体的判断逻辑为:以日志空间类SpaceInfo实例所使用的类加载器对应的classpath中是否存在指定类型的日志类库为准。
此处需要注意的是,如果在classpath路径上同时存在了logback、log4j、log4j2、commons-logging类库,则这些类库的优先级为logback、log4j2、log4j、commons-logging,即优先使用logback,然后log4j2,依次类推。
通过设置某个日志类库的禁用标志(上述类库的禁用标志分别为logback.middleware.log.disable、log4j2.middleware.log.disable、log4j2.middleware.log.disable、log4j.commons.middleware.log.disable)来禁止使用某个日志类库。默认都为false,表示都处于可以使用状态。如果都为true,表示都处于禁止使用状态,则使用默认的日志空间NOP,同上述sofa.middleware.log.disable为true时的情况。
以logback类库为例,使用日志环境工具类LogEnvUtils的isLog4jUsable方法,判断是否使用logback类库:
1. public static booleanisLogbackUsable(ClassLoader spaceClassloader) {
2. AssertUtil.notNull(spaceClassloader);
3. try {
4. return spaceClassloader.loadClass("ch.qos.logback.classic.LoggerContext")!= null;
5. } catch (ClassNotFoundException e) {
6. return false;
7. }
8. }
由于Infra模块classpath路径上存在logback类库,所以此处判断结果为true。
接下来,判断logback.middleware.log.disable属性是否为true。如果为true,则表示禁止中间件使用logback日志类库,则继续判断是否使用log4j2日志类库,依次类推。此处为false,则使用logback日志类库。
为名称为com.alipay.sofa.infra的日志空间创建类型为logback的日志空间Builder,即LoggerSpaceFactory4LogbackBuilder实例。
在此需要注意的是,不同类型的日志类库,对应的Builder不同,具体如下:
1. logback:LoggerSpaceFactory4LogbackBuilder;
2. log4j2:LoggerSpaceFactory4Log4j2Builder;
3. log4j:LoggerSpaceFactory4Log4jBuilder;
4. commons-logging:LoggerSpaceFactory4CommonsLoggingBuilder;
虽然为不同类型的日志类库创建日志空间工厂类的具体方法不同,但大同小异。在此以LoggerSpaceFactory4LogbackBuilder类为例,详细说明logback日志空间工厂类的创建方法。对于其它类型的Builder,就不同详述了。
调用LoggerSpaceFactory4LogbackBuilder类build方法,为名字为com.alipay.sofa.infra日志空间构建日志空间工厂类:
1. public AbstractLoggerSpaceFactorybuild(String spaceName, ClassLoader spaceClassloader) {
2. ……略
3. //load config file
4. URL configFileUrl = getSpaceLogConfigFileURL(spaceClassloader,spaceName);
5.
6. // set default logging.level
7. specifySpaceLogConfigProperites(spaceName);
8.
9. return doBuild(spaceName, spaceClassloader,configFileUrl);
10.
11. }
首先,加载中间件的日志配置文件,并返回配置文件的URL。
此处采用项目依赖方式,所以logback日志配置文件的URL为:
1. file:/D:/SOFA_Workspace/sofa-boot-master/infra-sofa-boot-starter/target/classes/com/alipay/sofa/infra/log/logback/log-conf.xml
以logback为例,其配置文件参考内容为:
1. <configuration>
2. <!-- logback appenders -->
3.
4. <!-- 按照每天生成日志文件 -->
5. <appendername="ERROR-APPENDER" class="ch.qos.logback.core.rolling.RollingFileAppender">
6. <append>true</append>
7. <!-- 过滤器,只记录 error 级别的日志 -->
8. <filterclass="ch.qos.logback.classic.filter.ThresholdFilter">
9. <level>error</level>
10. </filter>
11. <!-- 日志名称 -->
12. <file>${logging.path}/infra/common-error.log</file>
13. <!-- 每天生成一个日志文件,保存30天的日志文件 -->
14. <rollingPolicyclass="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
15. <!--日志文件输出的文件名:按天回滚 daily -->
16. <FileNamePattern>${logging.path}/infra/common-error.log.%d{yyyy-MM-dd}</FileNamePattern>
17. <!--日志文件保留天数-->
18. <MaxHistory>30</MaxHistory>
19. </rollingPolicy>
20. <encoderclass="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
21. <pattern>%d %-5p %-32t -%m%n</pattern>
22. <!-- 编码 -->
23. <charset>${file.encoding}</charset>
24. </encoder>
25. </appender>
26.
27. <appender name="ROOT-APPENDER"class="ch.qos.logback.core.rolling.RollingFileAppender">
28. <append>true</append>
29. <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
30. <level>${logging.level.com.alipay.sofa.infra}</level>
31. </filter>
32. <file>${logging.path}/infra/common-default.log</file>
33. <!-- 每天生成一个日志文件,保存30天的日志文件 -->
34. <rollingPolicyclass="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
35. <!--日志文件输出的文件名:按天回滚 daily -->
36. <FileNamePattern>${logging.path}/infra/common-default.log.%d{yyyy-MM-dd}</FileNamePattern>
37. <!--日志文件保留天数-->
38. <MaxHistory>30</MaxHistory>
39. </rollingPolicy>
40. <encoderclass="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
41. <pattern>%d %-5p %-32t -%m%n</pattern>
42. <!-- 编码 -->
43. <charset>${file.encoding}</charset>
44. </encoder>
45. </appender>
46.
47. <rootlevel="${logging.level.com.alipay.sofa.infra}">
48. <appender-refref="ROOT-APPENDER"/>
49. <appender-refref="ERROR-APPENDER"/>
50. </root>
51. </configuration>
上述内容为logback标准配置,在此不详述。
其次,调用specifySpaceLogConfigProperites方法,整理SpaceInfo的properties属性集合,规则如下:
1. 如果System.properties 与 SpaceInfo.properites 都含有某个配置,那么以System.properties为准,SpaceInfo.properites中重复定义会被抛弃;
2. 如果系统属性中无logging.path.com.alipay.sofa.infra属性值,但存在logging.path属性值,并且SpaceInfo属性中无logging.path.com.alipay.sofa.infra属性值,则在SpaceInfo.properites中设置日志路径为系统属性中logging.path的值。此处为logging.path.com.alipay.sofa.infra=./logs;
3. 如果系统属性中无logging.level.com.alipay.sofa.infra属性值,并且SpaceInfo属性中也无logging.level.com.alipay.sofa.infra属性值,则在SpaceInfo.properites中设置logging.level.com.alipay.sofa.infra的日志级别为默认的日志级别INFO。
最后,调用LoggerSpaceFactory4LogbackBuilder类doBuild方法:
1. public AbstractLoggerSpaceFactorydoBuild(String spaceName, ClassLoader spaceClassloader,
2. URLurl) {
3. final LoggerContext loggerContext = newLoggerContext();
4.
5. for (Map.Entry entry :getProperties().entrySet()) {
6. //from Map<String,String>
7. loggerContext.putProperty((String)entry.getKey(), (String) entry.getValue());
8. }
9.
10. if (url != null) {
11. try {
12. newContextInitializer(loggerContext).configureByResource(url);
13. } catch (JoranException e) {
14. throw newIllegalStateException("Logback loggerSpaceFactory build error", e);
15. }
16. }
17. return newAbstractLoggerSpaceFactory(getLoggingToolName()) {
18.
19. @Override
20. public Logger setLevel(String loggerName,AdapterLevel adapterLevel) throws Exception {
21. ch.qos.logback.classic.LoggerlogbackLogger = (ch.qos.logback.classic.Logger) this
22. .getLogger(loggerName);
23. ch.qos.logback.classic.LevellogbackLevel = this.toLogbackLevel(adapterLevel);
24. logbackLogger.setLevel(logbackLevel);
25. return logbackLogger;
26. }
27.
28. @Override
29. public Logger getLogger(String name) {
30. returnloggerContext.getLogger(name);
31. }
32.
33. privatech.qos.logback.classic.Level toLogbackLevel(AdapterLevel adapterLevel) {
34. if (adapterLevel == null) {
35. throw newIllegalStateException("AdapterLevel is NULL when adapter tologback.");
36. }
37. switch (adapterLevel) {
38. case TRACE:
39. returnch.qos.logback.classic.Level.TRACE;
40. case DEBUG:
41. returnch.qos.logback.classic.Level.DEBUG;
42. case INFO:
43. returnch.qos.logback.classic.Level.INFO;
44. case WARN:
45. returnch.qos.logback.classic.Level.WARN;
46. case ERROR:
47. returnch.qos.logback.classic.Level.ERROR;
48. default:
49. throw newIllegalStateException(adapterLevel
50. +" is unknown when adapter to logback.");
51. }
52. }
53. };
54. }
此方法开始为名称为com.alipay.sofa.infra的日志空间构建日志空间工厂类,主要步骤如下:
1. 为SOFABoot中间件创建LoggerContext实例,该实例为SOFABoot中间件的日志空间,管理该中间件所有logger;
2. 根据SpaceInfo的properties属性集合,设置LoggerContext实例的属性集合;
3. 如果日志配置文件URL不为null,则加载日志配置文件,并初始化LoggerContext实例;
4. 创建抽象类AbstractLoggerSpaceFactory的匿名实现类,类型为logback。该匿名类重写了AbstractLoggerSpaceFactory的setLevel和getLogger方法。在这两个方法中,使用logback相关的API完成设置日志级别和获取日志记录器logger的操作。
(二)获取日志记录器logger
再回看一下MultiAppLoggerSpaceManager类getLoggerBySpace方法:
1. public static LoggergetLoggerBySpace(String name, SpaceId spaceId, ClassLoader spaceClassloader) {
2. AbstractLoggerSpaceFactoryabstractLoggerSpaceFactory = getILoggerFactoryBySpaceName(
3. spaceId, spaceClassloader);
4. return abstractLoggerSpaceFactory.getLogger(name);
5. }
获取到AbstractLoggerSpaceFactory的实现类(此处为基于logback的实现)以后,调用其getLogger方法,获取名为com.alipay.sofa.infra.initializer.SOFABootInfrastructureSpringContextInitializer的日志记录器。
最后,在看看SOFABootInfrastructureSpringContextInitializer的initialize方法:
1. public voidinitialize(ConfigurableApplicationContext applicationContext) {
2. //init log
3. this.logInit(applicationContext.getEnvironment());
4.
5. InfraHealthCheckLoggerFactory.getLogger(
6. SOFABootInfrastructureSpringContextInitializer.class).info(
7. "SOFABoot InfrastructureStarting!");
8. }
调用logger的info方法,记录Infra模块启动开始日志,内容为SOFABoot Infrastructure Starting!。