一文带你了解Jcl日志框架
全称为Jakarta Commons Logging,是Apache提供的一个通用日志API。
它是为 "所有的Java日志实现"提供一个统一的接口,它自身也提供一个日志的实现,但是功能非常常弱(SimpleLog)。所以一般不会单独使用它。他允许开发人员使用不同的具体日志实现工具: Log4j, Jdk自带的日志(JUL)。
JCL 有两个基本的抽象类:Log(基本记录器)和LogFactory(负责创建Log实例)。

文章目录
- JCL入门
- 源码分析
- JCL原理分析
- 总结
JCL入门
- 建立maven工程
- 添加依赖
<dependencies><dependency><groupId>commons-logginggroupId><artifactId>commons-loggingartifactId><version>1.2version>dependency><dependency><groupId>junitgroupId><artifactId>junitartifactId><version>4.12version>dependency>dependencies><build><plugins><plugin><groupId>org.apache.maven.pluginsgroupId><artifactId>maven-compiler-pluginartifactId><version>3.8.1version><configuration><source>1.8source><target>1.8target><encoding>UTF—8encoding>configuration>plugin>plugins>build>
- 入门代码
@Test public void testQuick() throws Exception {// 创建日志对象/*** LogFactory是一个抽象类*/Log log = LogFactory.getLog(Jcl_Test.class);/*** 执行之后,先是获取了当前的类加载对象,进入了getInstance()方法,返回了当前类的全名称*/// 日志记录输出log.fatal("fatal");log.error("error");log.warn("warn");log.info("info");log.debug("debug");}
我们为什么要使用日志门面:
- 面向接口开发,不再依赖具体的实现类。减少代码的耦合
- 项目通过导入不同的日志实现类,可以灵活的切换日志框架
- 统一API,方便开发者学习和使用
- 统一配置便于项目日志的管理
源码分析
点Log接口 对应Log 接口按shift + Alt + b

进入JDK14Logger 类
//注意这里引入的类是JDK自带的日志JUL的Logger类
import java.util.logging.Level;
import java.util.logging.Logger;public class Jdk14Logger implements Log, Serializable {protected static final Level dummyLevel = Level.FINE;protected transient Logger logger = null;//默认为nullpublic Jdk14Logger(String name) {this.name = name;//调用了自身的getLogger()方法logger = getLogger();//见1}//1、获取Logger实例方法public Logger getLogger() {if (logger == null) {//重要:这里调用的是java.util.logging.Logger(即JUL)的获取实例方法logger = Logger.getLogger(name);}return logger;}//info日志等级调用public void info(Object message) {//实际上就是调用的JUL的INFO日志等级的Log方法log(Level.INFO, String.valueOf(message), null);}//fatal:我们之前在Log4j中看到是最高等级,这里也用来表示最高的意思public void fatal(Object message, Throwable exception) {//实际上就是调用JUL的SERVER日志等级(最高)的Log方法log(Level.SEVERE, String.valueOf(message), exception);}
}
jdk14Logger 类下的实现方法

看到这里其实心里就清楚了,原来就是外面套了个方法呀,通过继承Log接口来让Log接口统一管理各个日志框架,从而达到切换日志框架不改变代码的操作。那对于JCL中Log4j的方法我们也差不多知道了是如何实现了的。
先放入Log4j 的maven依赖
<dependency><groupId>log4jgroupId><artifactId>log4jartifactId><version>1.2.17version>
dependency>
进入Log4jLogger 界面

都实现了Log 接口 的日志等级方法
查看构造器以及静态方法代码块
//注意这里使用的是Log4j的日志
import org.apache.log4j.Logger;
import org.apache.log4j.Priority;
import org.apache.log4j.Level;public class Log4JLogger implements Log, Serializable {//默认为nullprivate transient volatile Logger logger = null;private static final String FQCN = Log4JLogger.class.getName();//类被加载时进行初始化static {if (!Priority.class.isAssignableFrom(Level.class)) {throw new InstantiationError("Log4J 1.2 not available");}Priority _traceLevel;try {} catch(Exception ex) {_traceLevel = Level.DEBUG;}traceLevel = _traceLevel;}//无参构造器public Log4JLogger() {name = null;}//有参构造器public Log4JLogger(String name) {this.name = name;//通过调用自身类的getLogger()获取this.logger = getLogger();//见1}//1、通过org.apache.log4j.Logger的getLogger()来获取实例public Logger getLogger() {Logger result = logger;if (result == null) {synchronized(this) {result = logger;if (result == null) {logger = result = Logger.getLogger(name);}}}return result;}//fatal等级方法就是调用的Log4j的FATAL等级,是一致的public void fatal(Object message) {getLogger().log(FQCN, Level.FATAL, message, null);}
}
JCL原理分析
分析如下代码块
@Test public void testQuick() throws Exception {// 创建日志对象Log log = LogFactory.getLog(Jcl_Test.class);// 日志记录输出log.fatal("fatal");log.error("error");log.warn("warn");log.info("info");log.debug("debug");}
点进LogFactory 抽象类
public abstract class LogFactory {//1、实际调用了其实现类的方法public static Log getLog(Class clazz) throws LogConfigurationException {//获取LogFactoryImplreturn getFactory().getInstance(clazz);//见2}
}public class LogFactoryImpl extends LogFactory {protected Constructor logConstructor = null;//该数组中包含了对应的全限定类名private static final String[] classesToDiscover = {LOGGING_IMPL_LOG4J_LOGGER,//"org.apache.commons.logging.impl.Log4JLogger""org.apache.commons.logging.impl.Jdk14Logger","org.apache.commons.logging.impl.Jdk13LumberjackLogger","org.apache.commons.logging.impl.SimpleLog"};//2、获取Log实例public Log getInstance(Class clazz) throws LogConfigurationException {//这里传入类的名称return getInstance(clazz.getName());//见3}//3、其中获取到Log实例public Log getInstance(String name) throws LogConfigurationException {Log instance = (Log) instances.get(name);if (instance == null) {//通过类名获取到Log实例instance = newInstance(name);//见4instances.put(name, instance);}return instance;}//4、获取新的实例protected Log newInstance(String name) throws LogConfigurationException {Log instance;try {//默认为nullif (logConstructor == null) {//重要:查找Log实现类,及获取Log的单个实现类instance = discoverLogImplementation(name);//见5}else {Object params[] = { name };instance = (Log) logConstructor.newInstance(params);}if (logMethod != null) {Object params[] = { this };logMethod.invoke(instance, params);}//返回获取到的Log实例即可return instance;} catch (LogConfigurationException lce) {....}//5、该方法通过数组的顺序来进行实现类实例private Log discoverLogImplementation(String logCategory)throws LogConfigurationException {...//无关紧要的先省略if (isDiagnosticsEnabled()) {logDiagnostic("No user-specified Log implementation; performing discovery" +" using the standard supported logging implementations...");}//核心部分:从数组中进行遍历,可见classesToDiscover数组内容(上面),顺序依次为Log4JLogger、Jdk14Logger...//其中的result == null则是判断是否获取到了实例,若获取到退出for循环for(int i=0; i<classesToDiscover.length && result == null; ++i) {result = createLogFromClass(classesToDiscover[i], logCategory, true);}if (result == null) {throw new LogConfigurationException("No suitable Log implementation");}//返回指定实例return result;}
第38行:调用方法获取到Log的实现类,确定使用的日志框架。
第73行:核心代码,根据多种框架实现类的权限定类名去查找获取Log实例的
说明:我们在调用LogFactory.getLog()方法时获取了JCL中Log接口的实现类实例,在实现类中的构造器里获取到了真正对应的日志框架Logger实例。
这样的话我们之前的第二部分就解释的通为什么默认获取到的是JUL,在添加了jar包之后则为Log4j了!!!
总结
1、JCL日志门面中Log是通用接口,LogFactory.getLog()用于获取对应Log的实现类。
2、Log接口(包含了主要的日志等级方法)的实现类主要的是两个,Jdk14Logger(即JUL,不导任何包默认)和Log4JLogger(即Log4j,导入Log4j则切换)。
3、对于获取Log实例是在LogFactory.getLog()中执行获取的,执行顺序见如下核心代码
private static final String[] classesToDiscover = {LOGGING_IMPL_LOG4J_LOGGER,//"org.apache.commons.logging.impl.Log4JLogger""org.apache.commons.logging.impl.Jdk14Logger","org.apache.commons.logging.impl.Jdk13LumberjackLogger","org.apache.commons.logging.impl.SimpleLog"
};//核心部分:从数组中进行遍历,可见classesToDiscover数组内容(上面),顺序依次为Log4JLogger、Jdk14Logger...
//其中的result == null则是判断是否获取到了实例,若获取到退出for循环
for(int i=0; i<classesToDiscover.length && result == null; ++i) {result = createLogFromClass(classesToDiscover[i], logCategory, true);
}
4、在获取到指定Log实例时,即使用构造器会进行各个实现类中的Logger实例的赋值获取(通过对应的日志框架),其中的日志等级方法调用的是对应框架的日志等级方法。
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
