JCL,全称为"Jakarta Commons Logging",也可称为"Apache Commons Logging",是Apache提供的一个通用日志API。JCL采用了设计模式中的“适配器模式”,它是为“所有的Java日志实现”提供的一个统一的接口,然后在适配类中将对日志的操作委托给具体的日志框架,它自身也提供一个日志的实现,但是功能非常弱(SimpleLog)。所以一般不会单独使用它。它允许开发人员使用不同的具体日志实现工具:Log4j,jdk自带的日志(JUL)
JCL有两个基本的抽象类:Log(基本记录器)和LogFactory(负责创建Log实例)
Log的继承体系如图1:
LogFactory的继承体系如图2:
Log4JLogger,Jdk14Logger等是适配类。
在Log4JLogger类中,包含有"org.apache.log4j.Logger"类,即Log4J中的Logger类,因而对Log4JLogger类中的日志操作方法的调用会被委托给"org.apache.log4j.Logger"类运行
在Jdk14Logger类中,包含有"java.util.logging.Logger"类,即Java Logging API中的Logger类,因而对Jdk14Logger类中的日志操作方法的调用会被委托给"java.util.logging.Logger"类运行
《jcl 与 jul、log4j1、log4j2、logback 集成》:https://jybzjf.iteye.com/blog/2238792
《Commons-Logging 存在的 ClassLoader 问题详解》:https://yq.aliyun.com/articles/46888
一、JCL入门
JCL+Java Logging API工程构建:
1.构建maven工程:JCL的jar包
commons-logging
commons-logging
1.2
2.创建TestDemo:JCL接口在没有其他日志实现的情况,会自动实现jdk自带的日志(JUL)
@Test
public void testQuick() throws Exception{
// 获取log日志记录器对象
Log log = LogFactory.getLog(JCLTest.class);
// 日志记录输出
log.info("hello jcl");
}
JCL+Log4J工程构建:
1.使用JCL接口实现log4j日志
首先在Maven工程中添加log4j日志实现的jar包
commons-logging
commons-logging
1.2
log4j
log4j
1.2.17
2.然后创建的配置文件commons-logging.properties和log4j.properties两个文件的内容
log4j.properties配置文件内容如下:
#配置根 Loggers控制日志的输出级别与日志是否输出
log4j.rootLogger=trace,console
#配置输出到控制台 Appenders是指定日志的输出方式
log4j.appender.console=org.apache.log4j.ConsoleAppender
#指定输出控制台
log4j.appender.console.Target = System.out
#指定布局,输出日志的格式
log4j.appender.console.layout=org.apache.log4j.PatternLayout
#指定布局的参数
log4j.appender.console.layout.ConversionPattern=[%-10p] %r %l %d{yyyy-MM-dd HH:mm:ss.SSS} %m%n
commons-logging.properties配置文件内容如下:
org.apache.commons.logging.Log = org.apache.commons.logging.impl.Log4JLogger
3.最后进行Demo测试,JCL会调用log4j实现,进行实现log4j的日志记录
@Test
public void testQuick() throws Exception{
// 获取log日志记录器对象
Log log = LogFactory.getLog(JCLTest.class);
// 日志记录输出
log.info("hello jcl");
}
commons-logging 绑定日志实现
LogFactory.getLog(JclTest.class) 的源码如下:
public static Log getLog(Class clazz) throws LogConfigurationException {
return getFactory().getInstance(clazz);
}
上述获取 Log 的过程大致分成 2 个阶段
来看下 commons-logging 包中的大概内容:
二、我们为什么要使用日志门面
1.面向接口开发,不再依赖具体的实现类。减少代码的耦合
2.项目通过导入不同的日志实现类,可以灵活的切换日志框架
3.统一API,方便开发者学习和使用
4.统一配置便于项目日志的管理
三、JCL原理
1.通过LogFactory动态加载Log实现类
2.1获取 LogFactory 的过程
从下面几种途径来获取 LogFactory
(1) 系统属性中获取
System.getProperty("org.apache.commons.logging.LogFactory")
(2) 使用 java 的 SPI 机制
对于 java 的 SPI 机制,详细内容可以自行搜索,这里不再说明。搜寻路径如下:
META-INF/services/org.apache.commons.logging.LogFactory
简单来说就是搜寻哪些 jar 包中含有搜寻含有上述文件,该文件中指明了对应的 LogFactory 实现
(3) 从 commons-logging 的配置文件
commons-logging 也是可以拥有自己的配置文件的,名字为 commons-logging.properties,只不过目前大多数情况下,我们都没有去使用它。如果使用了该配置文件,尝试从配置文件中读取属性 "org.apache.commons.logging.LogFactory" 对应的值
(4) 默认的 org.apache.commons.logging.impl.LogFactoryImpl
LogFactoryImpl 是 commons-logging 提供的默认实现
2.2根据 LogFactory 获取 Log 的过程
这时候就需要寻找底层是选用哪种类型的日志
就以 commons-logging 提供的默认实现为例,来详细看下这个过程:
(1) 从 commons-logging 的配置文件中寻找 Log 实现类的类名
从commons-logging.properties 配置文件中寻找属性为 "org.apache.commons.logging.Log" 对应的 Log 类名
(2) 从系统属性中寻找 Log 实现类的类名
System.getProperty("org.apache.commons.logging.Log")
(3) 如果上述方式没找到,则从 classesToDiscover 属性中寻找
classesToDiscover 属性值如下:
private static final String[] classesToDiscover = {
"org.apache.commons.logging.impl.Log4JLogger",
"org.apache.commons.logging.impl.Jdk14Logger",
"org.apache.commons.logging.impl.Jdk13LumberjackLogger",
"org.apache.commons.logging.impl.SimpleLog"
};
3.获取具体的日志实现,通过查看源代码可以发现,执行"LogFactory.getLog(Main.class)"语句的时候,最终是执行LogFactoryImp类中的discoverLogImplementation方法,在该方法中有如下代码语句:
for(int i = 0; i < classesToDiscover.length && result == null; ++i)
{
result = this.createLogFromClass(classesToDiscover[i], logCategory, true);
}
它会尝试根据上述类名,依次进行创建,如果能创建成功,则使用该 Log,然后返回给用户。