简短地描述下日志发展,最先出现的是apache开源社区的
log4j
,这个日志确实是应用最广泛的日志工具,成为了java日志的事实上的标准。然而,当时Sun公司在jdk1.4中增加了JUL日志实现,企图对抗log4j,但是却造成了混乱,这个也是被人诟病的一点。当然也有其他日志工具的出现,这样必然造成开发者的混乱,因为这些日志系统互相没有关联,替换和统一也就变成了比较棘手的一件事。想象下你的应用使用log4j,然后使用了一个其他团队的库,他们使用了JUL,你的应用就得使用两个日志系统了,然后又有第二个库出现了,使用了simplelog。这个时候估计让你崩溃了,这是要闹哪样?这个状况交给你来想想办法,你该如何解决呢?进行抽象,抽象出一个接口层,对每个日志实现都适配或者转接,这样这些提供给别人的库都直接使用抽象层即可。不错,开源社区提供了commons-logging
抽象,被称为JCL,也就是日志框架了,确实出色地完成了兼容主流的日志实现(log4j、JUL、simplelog),基本一统江湖,就连顶顶大名的spring也是依赖了JCL。看起来事物确实是美好,但是美好的日子不长,接下来另一个优秀的日志框架slf4j的加入导致了更加混乱的场面。比较巧的是slf4j的作者(Ceki Gülcü)就是log4j的作者,他觉得JCL不够优秀,所以他要自己搞一套更优雅的出来,于是slf4j日志体系诞生了,并为slf4j实现了一个亲子——logback,确实更加优雅,但是由于之前很多代码库已经使用JCL,虽然出现slf4j和JCL之间的桥接转换,但是集成的时候问题依然多多,对很多新手来说确实会很懊恼,因为比单独的log4j时代“复杂”多了,可以关注下这个,抱怨声确实很多。到此本来应该完了,但是Ceki Gülcü觉得还是得回头拯救下自己的“大阿哥”——log4j,于是log4j2诞生了,同样log4j2也参与到了slf4j日志体系中,想必将来会更加混乱。
1. 日志框架的选择
- 在非特殊的情况下,项目使用
slf4j-api+logback
。接入方式推荐SpringBoot2.x整合logback日志框架(1) - 对于要提供给别人的类库,建议使用
slf4j-api
,使用方可以自由选择具体的实现,并且建议类库不要依赖具体的日志实现,maven依赖如下图:
<!-- 日志接口类-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
- 项目中具体的使用,如下面代码:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class XxTemplate {
//顶级接口,实现依赖于具体的子类
private static final Logger logger = LoggerFactory.getLogger(XxTemplate.class);
public void get() {
logger.info("打印参数");
}
}
2. slf4j的设计
slf4j:Simple Logging Facade for Java
,为java提供的简单日志Facade。Facade门面,更底层一点说就是接口。它允许用户以自己的喜好,在工程中通过slf4j接入不同的日志系统。
在简述中,可以知道日志发展是比较混乱的,slf4j作者想将世面的多种日志框架整合起来。对于slf4j前的具体实现。提供了一些桥接类,如下图:
image.png-
每个系统的日志顶级接口都是slf4j-api。
-
第三层的深蓝色块,是具体的日志实现。他们本身都实现了slf4j API。slf4j-simple.jar和slf4j-nop.jar这两个不用多说,看名字就知道一个是slf4j的简单实现,一个是slf4j的空实现,平时用处也不大。而logback之所以也实现了slf4j API,据说是因为logback和slf4j出自同一人之手,这人同时也是log4j的作者。
-
第三层的浅蓝色块,是适配器,左边的slf4j-log4j12.jar桥接器看名字就知道是slf4j到log4j的桥接器,同样,右边的slf4j-jdk14.jar就是slf4j到Java原生日志实现的桥接器了。它们的下一层分别是对应的日志框架实现,
log4j的实现代码是log4j.jar,而jul实现代码已经包含在了JVM runtime中,不需要单独的jar包。
-
第三层中灰色的jar包块都实现了
slf4j API
,只是浅蓝色的桥接器快对slf4j API
的实现并不是直接输出日志,而是转去调用别的日志框架的实现。
3. slf4j的实现原理
slf4j-api和具体的实现类是在编译时进行绑定的,slf4j-api中会去调用StaticLoggerBinder
这个类获取绑定的工厂类,而每个日志实现会在自己的jar中提供这样一个类,这样slf4j-api就实现了编译时绑定实现。
slf4j-api
的pom文件,在打包的时候,将该类删除,最终走子类实现的StaticLoggerBinder
类。
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
<executions>
<execution>
<phase>process-classes</phase>
<goals>
<goal>run</goal>
</goals>
</execution>
</executions>
<configuration>
<tasks>
<echo>Removing slf4j-api's dummy StaticLoggerBinder and StaticMarkerBinder</echo>
<delete dir="target/classes/org/slf4j/impl"/>
</tasks>
</configuration>
</plugin>
包里没有该文件.png
打出来的slf4j-api是不完整的,只有找到包含StaticLoggerBinder这个类的包才可以,于是slf4j-log4j和logback-classic都提供了这个类。另外,slf4j-log4j和logback以及slf4j-jdk14是不能同时和slf4j共存的,也就是说只能有一个实现存在,不然启动会提示有多个绑定。
网友评论