一、简述
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文件内容如下: