目录
一、主流的log技术名词
1.1 log4j
1.2 JUL
1.3 JCL
1.4 SLF4J
1.4.1 绑定器
1.4.2 桥接器
1.5 log4j2
1.6 logback
1.7 simple-log
1.8 各种日志技术的关系和作用
二、spring日志技术分析
2.1 spring4日志技术实现
2.1 spring5日志技术实现
maven依赖:
log4j
log4j
1.2.17
可以不需要依赖第三方的技术,直接记录日志,但是必须要有配置文件。
使用:
// 引用的是log4j的Logger
import org.apache.log4j.Logger;
public class Log4j {
public static void main(String[] args) {
// 获取log4j的Logger对象,后面的括号是设置日志对象的唯一标识,可以自定义命名或当前类的Class
Logger logger = Logger.getLogger(Log4j.class);
logger.info("log4j");
}
}
效果:
java自带的一个日志记录的技术,可以直接使用,不需要引用依赖包
使用:
// 这里引用的是JUL包下的Logger
import java.util.logging.Logger;
/**
* 使用JUL记录日志不需要引用外部包,因为JUL是jdk自带的
*
* 不使用配置文件也可以使用
*/
public class JUL {
public static void main(String[] args) {
// JUL的Logger对象只能为其设置字符串命名,不能使用Class作为其标识
Logger logger = Logger.getLogger("JUL");
logger.info("jul");
}
}
效果:
jakarta Commons LoggingImpl
maven依赖:
commons-logging
commons-logging
1.1.1
jcl它不直接记录日志,他是通过第三方记录日志比如log4j,jul等
如果使用jcl来记录日志,在没有log4j的依赖情况下,是用jul
如果有了log4依赖则优先使用使用log4j
使用:
// JCL的日志对象是Log
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
public class JCL {
public static void main(String[] args) {
// JCL的日志对象是log,通过LogFactory得到的,括号里面设置的是日志对象的唯一标识,既可以使用当前类的class也可以自定义名字
Log log = LogFactory.getLog("JCL");
log.info("jcl");
}
}
没有log4j依赖就会使用JUL:
有log4j依赖就会使用log4j:
JCL=Jakarta commons-logging ,是apache公司开发的一个抽象日志通用框架,本身不实现日志记录,但是提供了记录日志的抽象方法即接口(info,debug,error.......),底层通过一个数组存放具体的日志框架的类名,然后循环数组依次去匹配这些类名是否在app中被依赖了,如果找到被依赖的则直接使用,所以他有先后顺序。
上图为JCL中存放日志技术类名的数组,默认有四个,后面两个可以忽略,因为已经很老的技术了,现在不怎么用了。总之使用JCL的话,它对日志技术的优先使用顺序是log4j>jul
上图81行就是通过一个类名去load一个class,如果load成功则直接new出来并且返回使用(只有项目引用了相应的依赖,类加载器才会把对应的Class加载入JVM,才可以使用forName将日志对象实例反射创建)。
如果没有load到class这循环第二个(因为那四个类名是顺序存放在那个数组当中的),直到找到为止。
可以看到这里的循环条件必须满足result不为空,
也就是如果没有找到具体的日志依赖则继续循环,如果找到则条件不成立,不进行循环了。
使用JCL的优点:比如我们有一个项目,A模块用的是log4j,B模块用的是jul,如果直接使用这两个包记录日志,那么项目合并之后日记会出现不统一,非常混乱,不容易维护等问题。但是如果A模块使用JUL来调用log4j,B模块使用JUL来调用jul,那么两个模块合并之后就能很方便的统一管理,因为都是利用JUL中的Log作为中间件来进行日志管理的。所以使用一个中间件来管理项目日志的话,从长远角度看,对项目的扩展性是有好处的。
spring4和spring5都内置了JCL
现在JCL已经不更新了,逐渐被淘汰了。现在slf4j取代了JCL,它据有更好的可拓展性
slf4j他也不记录日志,通过绑定器绑定一个具体的日志包来完成日志记录
它引入了两个新的概念:绑定器(binder)和桥接器(bridge)
它不再像JCL那样固定只能支持4中日志包,它可以通过绑定器更加灵活的拓展,每一种日志包有对应一个SLF4J绑定器,需要用什么日志包来实现记录日志的功能,就是用对应的绑定器就可以了。所以更加容易拓展,如果出现了新的日志类,只需要编写对应的绑定器就可以关联到SLF4J进行使用了。
使用:
maven依赖:
org.slf4j
slf4j-api
1.7.25
绑定器依赖
org.slf4j
slf4j-jdk14
1.7.25
org.slf4j
slf4j-log4j12
1.7.25
org.slf4j
slf4j-jcl
1.7.25
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class SLF4J {
public static void main(String[] args) {
// JCL的日志对象是logger,也是通过LoggerFactory获得的,既可以使用当前类的class作为日志对象标识也可以使用自定义命名
Logger logger = LoggerFactory.getLogger(SLF4J.class);
logger.info("slf4j");
}
}
桥接器指的是可以将一种日志类的实现转接到SLF4J上来,可以方便实现系统的日志统一管理.
比如使用了log4j的桥接器,那么A模块使用的是log4j,B模块使用的是slf4j,使用JUL绑定器,那么A模块的log4j日志系统就会转接到slf4j中,然后在从slf4j中使用JUL绑定器来完成日志记录,也就将log4j转换成了JUL。
桥接器依赖:
org.slf4j
jcl-over-slf4j
1.7.25
org.slf4j
log4j-over-slf4j
1.7.25
添加了桥接器之后,就可以使桥接器对应的日志记录实现转接到SLF4J上
缺点:
在项目变复杂的时候就有可能出现循环引用的问题,进而导致栈溢出。
比如:我将SLF4J使用一个绑定到JCL的绑定器,再使用一个JCL的桥接器将JCL再转接到SLF4J上,这样就会出现循环引用的问题
spring4当中依赖的是原生的JCL,具体工作原理上面已经讲了
spring5使用的spring的JCL(spring改了jcl的代码)来记录日志的,但是jcl不能直接记录日志,也是采用一个优先的原则来选择日志包。
spring5将JCL改掉了,不再是原生的那样使用for循环,而是用一个switch选择。
源码:
在抽象类LogFactory中生成log对象
public static Log getLog(String name) {
switch(logApi) {
case LOG4J:
return LogFactory.Log4jDelegate.createLog(name);
case SLF4J_LAL:
return LogFactory.Slf4jDelegate.createLocationAwareLog(name);
case SLF4J:
return LogFactory.Slf4jDelegate.createLog(name);
default:
return LogFactory.JavaUtilDelegate.createLog(name);
}
}
由上面的源码可知,它是通过判断logApi的值来选择使用的日志包的。而logApi的值是在这个类的静态方法中生成的
static {
logApi = LogFactory.LogApi.JUL;
ClassLoader cl = LogFactory.class.getClassLoader();
try {
// 这里需要注意,这里是log4j2
cl.loadClass("org.apache.logging.log4j.spi.ExtendedLogger");
logApi = LogFactory.LogApi.LOG4J;
} catch (ClassNotFoundException var6) {
try {
cl.loadClass("org.slf4j.spi.LocationAwareLogger");
logApi = LogFactory.LogApi.SLF4J_LAL;
} catch (ClassNotFoundException var5) {
try {
cl.loadClass("org.slf4j.Logger");
logApi = LogFactory.LogApi.SLF4J;
} catch (ClassNotFoundException var4) {
}
}
}
}
它获当前类加载器,然后按照一定的优先顺序依次根据类全限定名加载,如果项目引入了相应的依赖,将class加载入了JVM,那么就可以成功给logApi赋值。但是这里有一点需要注意,spring5中的JCL中引用的是log4j2,不是log4j,所以直接通过引用log4j的依赖包在spring5项目中是不起作用的,如果想要在spring5中使用log4j就得通过引用SLF4J,然后再通过引用log4j的绑定包来使用log4j,这是sping5与sping4在日志系统上最大的不同。