1、 spring-jcl模块结构
可以看到,这个模块的包名是apache的commons.logging,因为这是spring团队对common.logging 进行了重写,所以包名还保留之前的apache.commons.logging。
这个模块非常简单,对外其实就提供了一个LogFactory类,提供两个方法,得到 Log的接口的实例
public abstract class LogFactory {
public static Log getLog(Class<?> clazz) {
return getLog(clazz.getName());
}
public static Log getLog(String name) {
return LogAdapter.createLog(name);
}
后面还有几个方法,被弃用了
}
这里可以看到这其实就一个包装,真正的实现逻辑在LogAdapter里面
下面我们看一下LogAdapter类,
(简化版本)
final class LogAdapter {
静态代码块
作用:根据classpath加载类,来决定采用什么Log日志实现,对枚举类赋值。
/// 这里定义了 一个枚举类:LogApi
private enum LogApi {LOG4J, SLF4J_LAL, SLF4J, JUL}
static {
if (isPresent("org.apache.logging.log4j.spi.ExtendedLogger")) {
/// 说明有 log4j-api jar包
if (isPresent("org.apache.logging.slf4j.SLF4JProvider") && isPresent("org.slf4j.spi.LocationAwareLogger")) {
// 如果有log4j-to-slf4j jar包,也有 slf4j-api 那就采用 slf4j
// 这里总结就是 如果有log4j 也有 log4j-to-slf4j ,那就直接用slf4j日志
logApi = LogApi.SLF4J_LAL;
}
else {
/// 如果不存在 log4j转 slf4j 的话,那就采用log4j来进行日志记录
/// 所以总得来说 log4j的优先级比较高
logApi = LogApi.LOG4J;
}
}
else if (isPresent("org.slf4j.spi.LocationAwareLogger")) {
/// 这里是对应 slf4j 1.3以及以后的版本,因为在1.3以后才有了LocationAwareLogger类
logApi = LogApi.SLF4J_LAL;
}
else if (isPresent("org.slf4j.Logger")) {
/// 这里是对应 slf4j 1.3之前的版本,之前的版本没有LocationAwareLogger类
logApi = LogApi.SLF4J;
}
else {
默认采用 jdk的 日志类 jul
logApi = LogApi.JUL;
}
}
/// 提供给LogFactory类调用
private static class Log4jAdapter {
public static Log createLog(String name) {
return new Log4jLog(name);
}
}
根据 枚举类型,决定采用哪种类型的Log
这里有 由 三个Adapter内部类 来完成创建
public static Log createLog(String name) {
switch (logApi) {
case LOG4J:
return Log4jAdapter.createLog(name);
case SLF4J_LAL:
return Slf4jAdapter.createLocationAwareLog(name);
case SLF4J:
return Slf4jAdapter.createLog(name);
default:
return JavaUtilAdapter.createLog(name);
}
}
/ 下面三个Adapter内部类,用来返回不同Log接口的实现类
private static class Log4jAdapter{
/// 这个内部类会返回的Log接口的实现对象,底层会调用Log4j 日志实现
}
private static class Slf4jAdapter{
/// 这个类 两个方法 返回的Log接口的实现对象,底层会调用 Slf4j的机制,在找到真正的日志实现组建
public static Log createLocationAwareLog(String name){
}
public static Log createLog(String name){
}
}
private static class JavaUtilAdapter{
这个内部类,返回的Log接口的实现类,底层调用 jdk自带的 日志实现,即 jul(java.util.log)
}
/ 四个Log接口的实现内部类 分布对应上面三个Adapter的返回对象 这里就不写了
private static class Log4jLog implements Log, Serializable{}
private static class Slf4jLog<T extends Logger> implements Log, Serializable {}
private static class Slf4jLocationAwareLog extends Slf4jLog<LocationAwareLogger> implements Serializable{}
private static class JavaUtilLog implements Log, Serializable
/// 这个类是 为jdk的日志服务的
private static class LocationResolvingLogRecord extends LogRecord
}
2、static代码块逻辑(决定了底层采用什么日志类型)
关于static代码块里面的处理逻辑:
这里有一个优先级:LOG4J2 级是最高的,其次是SLF4J (1.3版本及以后)、SLF4J(1.3版本之前)、JUL
这里有一个逻辑比较饶: 如果有LOG4J2 但是还存在SLF4J和log4j-to-slf4j.jar,那么就直接采用SLF4J。 因为SLF4J和log4j-to-slf4j.jar的存在,就会使得LOG4J2最终由SLF4J实现,所以Spring就直接采用SLF4J了。
总结: spring-jcl 是对Apache Commons Logging 的改造版本,采用的设计模式是“适配器模式”,它对外提供统一的接口,然后在适配类中将对日志的操作委托给具体的日志框架。
在Spring-JCL中对外有两个统一的接口,分别是Log和LogFactory。
LogFactory返回的Log的实现是由运行时决定的,可能有SLF4J 、LOG4J2、JUL中的一种实现。
3、关于java的日志系统
简单说一下:
关于java日志系统的使用总的来说有两种:
一种:代码中直接使用固定日志框架,例如JUL、Log4J1、Log4J2、Logback
另一种:代码中使用日志接口,例如SLF4J、commons-logging
日志实现框架有:
1、JDK自带的logging日志框架 java.util.logging,简称 JUL
2、Log4J1:
3、Log4J2 :Log4j1的升级版,发生了很大的变化,不兼容。并且推出了Log4J-api的面向接口编程
4、Logback:SLF4J提的一种日志框架
日志接口框架有:
1、apache的commons-logging
2、SLF4J
日志接口是为各种loging APIs提供一个简单统一的接口,从而使得最终用户能够在部署的时候配置自己希望的loging APIs实现
本来还想写点关于java中日志的一些对比,但是后来看到了下面这篇文章,就放弃了
因为写得太好了,无法超越啊 😄
强烈推荐大家看乒乓狂魔的这篇文章(共4章):jdk-logging、log4j、logback日志介绍及原理