spring5新特性日志体系
主流的log技术名词
1.jul
java自带的一个日志记录的技术,直接使用
java.util.logging.Logger
2.log4j
1 | ini复制代码//log4j依赖 |
log4j特点:
可以不需要依赖第三方的技术,直接记录日志。
3.jcl
1 | typescript复制代码//jcl依赖 |
这个jcl什么情况下会有不同的输出?
1.当项目有logj4的依赖的时候,就会输出(1)信息。
2.当项目没有log4j依赖的时候,就会使用java自带的日志技术jul输出(2)信息。
- jcl源码分析:
1 | ini复制代码#LogFactory |
通过分析jcl的代码可以得到:
jcl本身不实现日志记录,但是提供了记录日志的抽象方法即接口(info,debug,error…….)
底层通过一个数组存放具体的日志框架的类名,然后循环数组依次去匹配这些类名是否在项目中被依赖了,如果找到被依赖的则直接使用,所以他有先后顺序。
下图为jcl中存放日志技术类名的数组,默认有四个,后面两个可以忽略。
上面的代码81行就是通过一个类名去load一个class,如果load成功则直接new出来并且返回使用。如果没有load到class这循环第二个,直到找到为止。
可以看到这里的循环条件必须满足result不为空,也就是如果没有找到具体的日志依赖则继续循环,如果找到则条件不成立,不进行循环了。
总结:顺序log4j>jul
- 虽然Log4JLogger是jcl的jar包中的类,但是该Log4JLogger类,依赖了log4j的类,当你没有引入log4j的依赖的时候,在创建Log4JLogger类,就会失败。
- 下图是在没有引入log4j依赖的情况下,Log4JLogger类的情况图:
- 因为jul是java自带的日志类,所以在java环境下,jcl就算在创建log4j失败的情况下,也会去创建Jdk14Logger。可以看到Jdk14Logger是依赖了jul的。下图是Jdk14Logger的类图:
jcl特点:
他不直接记录日志,他是通过第三方记录日志(jul)。
jcl是一个接口,默认有4个log实现类。
4.slf4j
slf4j他也不记录日志,通过绑定器绑定一个具体的日志记录来完成日志记录
官网:www.slf4j.org/
1 | xml复制代码//slf4j依赖 |
- 在只添加了slf4j依赖,而没有添加任意一个绑定器,日志是不会打印的。控制台会输出warn信息,如下图:
1 | xml复制代码//slf4j依赖 |
- slf4j提供了很多的绑定器,有log4j,jul,jcl。
- 当引入了jul绑定器之后,slf4j就能打印日志了,如下图:
总结:
- slf4j需要打印日志,就一定需要引入绑定器。slf4j提供了很多的绑定器,有jul,jcl,log4j等。
- slf4j如果引入了jcl绑定器,因为jcl也是一个接口,jcl会加载log4j,jul。
- 如果你想使用log4j,也需要引入log4j的依赖,log4j的配置文件
- 如果你不引入log4j的依赖,就默认使用jul
- slf4j如果引入了log4j绑定器,需要log4j的配置文件(这个时候不用引入log4j的依赖了,因为该绑定器已经帮我们引入了)
- 问题:有这样的一个业务系统:
- 项目A,是使用了slf4j打印日志,然后通过slf4j绑定器,绑定到jul,然后使用jul打印日志。
- 项目A使用了spring框架,但是spring框架使用jcl打印日志,spring引入了log4j依赖,使得spring框架是用log4j打印日志。
- 这样的话,项目A就出现了多个日志框架打印日志了,那就很混乱,现在要求项目A只能使用一种日志框架技术,这个要怎么处理?
1 | xml复制代码//项目A的依赖 |
- 就引入上面的依赖jar包,项目就会有两种日志输出,如下图所示:
解决方案:
- 可以通过修改slf4j的绑定器,直接改用slf4j的log4j绑定器。
因为使用slf4j绑定到log4j,只需要简单的引入一个依赖即可。
- 使用slf4j的桥接器,将spring使用jcl打印日志这步切断,将jclj桥接到slf4j,然后再走项目A的日志打印。
因为使用jcl桥接到slf4j,只需要简单的引入一个依赖即可。
1 | xml复制代码//增加下面这个依赖即可: |
- 当使用了jcl-over-slf4j桥接器之后,可以使得jcl桥接到slf4j,然后再走slf4j这边的日志输出。从而实现将spring框架的日志输出,改为了slf4j–>jul
- 改进后的日志打印流程图如下:
各种日志技术的关系和作用
spring5新特性日志体系
Spring4当中依赖jcl,即Spring4当中采用的日志技术是jcl:commons-logging,即默认使用jul;加入log4j依赖和配置,即可切换为log4j
Spring5当中也是使用了jcl:spring-jcl,是重写为了jul框架。spring5使用的spring的jcl(spring改了jcl的代码)来记录日志的,但是jcl不能直接记录日志,采用循环优先的原则。
spring-jcl源码解析
1 | java复制代码#AbstractApplicationContext |
从上面spring5的源码可以看到,spring5使用的日志是spring-jcl,默认是jul,然后会依次加载log4j2,slf4j。在都加载不到的情况下,就使用默认的jul日志技术了。
因为spring5使用的是log4j2,所以在加入了log4j的依赖和配置文件,是不生效的。
- spring5使用log4j2日志技术,需要加入的依赖和配置文件。
1 | xml复制代码<!-- spring5的依赖 --> |
- 要将spring5改用log4j来记录日志,怎么实现?
- 1.引入slf4j依赖,然后slf4j绑定log4j,并添加log4j的配置文件。
1 | xml复制代码<!-- spring5的依赖 --> |
扩展:
Mybatis日志体系
- mybatis的日志技术实现
1 | typescript复制代码#LogFactory |
具体实现类
mybatis提供很多日志的实现类,用来记录日志,取决于初始化的时候load到的class
- jcl实现
- log4j2实现
- jul实现
mybaits缓存问题:
1.mybaits在整合spring框架的时候,一级缓存会失效,原因是mybatis一级缓存是基于sqlSession的,整合了spring之后,spring会管理sqlSession,在查询完之后,会帮我们关闭sqlSession,所以导致缓存失效了。2.mybatis的二级缓存。开启也是很简单在对应的mapper接口中加上@CacheNamespace注解即可。
备注:mybatis的二级缓存会有一个很大的坑。
因为mybatis的二级缓存是基于命名空间来实现了。当在不同的mapper接口操作了同一张表,这个就会有问题了,A接口更新了数据,B接口两次获取的数据都会是一样的。
mybatis打印sql日志分析
mybaits加载日志技术的顺序是:slf4j–>jcl—>log4j2—>log4j–>jul—>nolog
1.在spring4+mybatis+log4j的情况会有sql日志输出。因为spring4使用的是jcl,jcl在引入log4j依赖下,就会使用了log4j技术打印sql日志。
2.在spring5+mybatis+log4j的情况不会有sql日志输出。
因为spring5使用的是spring-jcl(本质也是jcl),spring-jcl默认使用了jul(不再使用log4j而是用log4j2,在上面有详细说明)。
由于mybatis加载日志的顺序,jcl是先与log4j,所以该情况下导致了mybatis使用了jcl技术。
那么问题来了?为什么mybatis在使用jul的情况下,没有打印sql日志?
当使用了jul的时候,jul默认的日志级别是 INFO
原生的jdk的logging的日志级别是FINEST、FINE、INFO、WARNING、SEVERE分别对应我们常见的trace、debug、info、warn、error
- 通过下图可以看到jdk14的isDebugEnabled()方法,是使用了Level.FINE,这个值是500
- 跟踪isLoggable()方法,可以看到下图,level.intValue()就是上面传递进去的Level.FINE是500,levelValue是800
- 这里跟踪一下levelValue的值,可以看到levelValue = Level.INFO.intValue() , INFO的值是800
- 跟踪mybatis打印sql日志的源码,是判断了isDebugEnabled()返回的值,所以mybatis在使用jul的时候,是不能打印sql日志的。
本文转载自: 掘金