前言
最近想系统学习一下spring框架的知识,搭完框架运行第一个 HelloWorld 的例子后。我发现要查看 spring 启动流程需要查
看启动日志。以前使用的时候都是照抄网上的配置使用,不知道到底是怎么工作的。后来上网查了一下,发现 Java 日志
组件居然有好几种:
- logging (JDK14提供) 被强大的三方日志组件给埋没
- log4j(Apache的开源项目log4j)
- log4j2(log4j的重构,提升性能,具体参见log4j2改善)
- logback(和log4j对比)
- common-logging (通用的日志接口,动态加载绑定)
- slf4j(通用的日志接口,编译静态加载绑定)
上面说了4种日志实现和两种通用接口.对于这么多日志框架,和一大堆maven下面的依赖包。我们应该怎么选择呢?
common-logging(动态接口)
common-logging 是apache提供的一个通用的日志接口。用户可以自由选择第三方的日志
组件作为具体实现,像log4j,
或者JDK自带的logging, common-logging会通过动态查找的机制,在程序运行时
自动找出真正使用的日志库。当然,
common-logging内部有一个simple logger的简单实现,但是功能很弱。所以使用
common-logging,通常都是配合着
log4j来使用。使用它的好处就是,代码依赖是common-logging而非log4j, 避免了和具体
的日志方案直接耦合,在有必要时,可以更改日志实现的第三方库。
longging使用代码
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
public class A {
private static Log logger = LogFactory.getLog(this.getClass());
}
spring-core默认使用的就是common-logging.该通用接口默认有以下实现 (不支持logback)
动态查找原理:
Log 是一个接口声明。LogFactory 的内部会去装载具体的日志系统,并获得
实现该Log 接口的实现类。LogFactory 内部装载日志系统的流程如下:
首先,寻找org.apache.commons.logging.LogFactory 属性配置。
否则,利用JDK1.3 开始提供的 service 发现机制,会扫描 classpah 下的METAINF/services/org.apache.commons.logging.LogFactory文件,若找到则装载里面的配置,使用里面
的配置。否则,从Classpath 里寻找commons-logging.properties ,找到则根据里面的配置加载。
-
否则,使用默认的配置:如果能找到 Log4j 则默认使用 Log4j 实现,如果没有则使 JDK14Logge 实现,再没有则使
用 commons-logging 内部提供的 SimpleLog 实现。
common-logging转向某个日志框架
commons-logging.jar 核心API(必选)
log4j-jcl.jar 绑定使用lo4j1(可选)
jcl-over-slf4j.jar 绑定使用slf4j(可选) 接口到接口 - -
slf4j(静态接口)
common-logging通过动态查找的机制,因此在不同classloader的情况下可能无法正常工作,所以就有slf4j这种编译时静态
绑定的框架。
slf4j是Java简单日志门面,类似JDBC的Driver,需要不同的日志框架提供实现才能正常工作
slf4j使用代码
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class A {
private static Log logger = LogFactory.getLog(this.getClass());
}
slf4j转向某个实际日志框架
slf4j-api.jar 核心接口(必选)
slf4j-log4j12.jar 绑定使用log4j1(可选)
slf4j-jdk14.jar 绑定使用jdk-logging(可选)
logback-classic.jar 绑定使用logback(可选)
log4j-slf4j-impl.jar 绑定使用log4j2(可选)
slf4j-jcl.jar 绑定使用commons-logging(可选) 接口又到了一个接口- -!
某日志框架转向slf4j###
比如spring-core内部使用的是common-logging接口。Google AppEngine 它使用的是 java.util.logging(JUL)这个时候
需要使用到桥接器,所谓的桥接器就是一个假的日志实现工具,比如当你把 jcl-over-slf4j.jar 放到 CLASS_PATH 时,即使某
个组件原本是通过 JCL 输出日志的,现在却会被 jcl-over-slf4j “骗到” slf4j 里,然后 slf4j 又会根据绑定器把日志交给
具体的日志实现工具
log4j-over-slf4j.jar log4j转向slf4j
jcl-over-slf4j.jar common-logging转向slf4j
jul-to-slf4j.jar jdk-logging转向slf4j
tip:这里值得注意的是当你使用log4j-over-slf4j.jar -> slf4j-api.jar -> log4j-core.jar时就会把日志踢来踢去进入死循环中
总结
java中繁多的日志框架和依赖包让人摸不着头脑,本文理清日志组件的相关历史和使用。在今后的项目中遇到日志依赖问题
也能够很好的解决。目前来说日志框架的典型搭配是
slf4j-api.jar slf4j 核心api
jcl-over-slf4j.jar common-logging桥接器
jul-to-slf4j.jar jdk-logging桥接器(如果项目中没有jdk-logging的日志可以不选用)
log4j-slf4j-impl.jar 绑定log4j2
log4j-core.jar log4j真正实现
log4j-api.jar log4japi
附上相关maven 依赖
org.apache.logging.log4j
log4j-slf4j-impl
${log4j2.version}
org.apache.logging.log4j
log4j-jcl
${log4j2.version}
org.apache.logging.log4j
log4j-api
${log4j2.version}
org.apache.logging.log4j
log4j-core
${log4j2.version}
org.slf4j
slf4j-api
1.7.21
最终效果就是..编程 API 使用的是 slf4j 而最终实现打印的是 log4j ,这样一来项目只需要配置 log4j2.xml , 如果需要转换某
种实现,只需要根据相关规则修改依赖包,不需要修改代码,因为是针对接口slf4j实现的