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

JCL入门
- 建立maven工程
- 添加依赖
<dependencies>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF—8</encoding>
</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;//默认为null
public 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>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</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 {
//默认为null
private 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 {
//获取LogFactoryImpl
return 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);//见4
instances.put(name, instance);
}
return instance;
}
//4、获取新的实例
protected Log newInstance(String name) throws LogConfigurationException {
Log instance;
try {
//默认为null
if (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实例的赋值获取(通过对应的日志框架),其中的日志等级方法调用的是对应框架的日志等级方法。

320

被折叠的 条评论
为什么被折叠?



