JAVA中的日志slf4j从0到1(slf4j-log4j和logback出现多个绑定的异常)

简短地描述下日志发展,最先出现的是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. 日志框架的选择

  1. 在非特殊的情况下,项目使用slf4j-api+logback。接入方式推荐SpringBoot2.x整合logback日志框架(1)
  2. 对于要提供给别人的类库,建议使用slf4j-api,使用方可以自由选择具体的实现,并且建议类库不要依赖具体的日志实现,maven依赖如下图:
        
        
            org.slf4j
            slf4j-api
        
  1. 项目中具体的使用,如下面代码:
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类。

      
        org.apache.maven.plugins
        maven-antrun-plugin
        
          
            process-classes
            
             run
            
          
        
        
          
            Removing slf4j-api's dummy StaticLoggerBinder and StaticMarkerBinder
            
          
        
      
包里没有该文件.png

打出来的slf4j-api是不完整的,只有找到包含StaticLoggerBinder这个类的包才可以,于是slf4j-log4j和logback-classic都提供了这个类。另外,slf4j-log4j和logback以及slf4j-jdk14是不能同时和slf4j共存的,也就是说只能有一个实现存在,不然启动会提示有多个绑定。

推荐阅读

  • log4j-over-slf4j与slf4j-log4j12共存stack overflow异常分析
  • java日志系统详解

你可能感兴趣的:(JAVA中的日志slf4j从0到1(slf4j-log4j和logback出现多个绑定的异常))