Java 日记框架

结绳记事,总结、思考,方有成长~

日志框架发展史

对于一个应用来说,日志是必不可少的一部分。程序一旦发布到线上,如果没有日志信息,那它就像个黑盒,而有了日志,就相当于有了一双洞察程序执行逻辑的眼睛。Java领域存在多种日志框架,现有的日志框架包括:Slf4j、Logback、Commons Logging、Log4j 1、Log4j 2、Java util logging。虽然有这么多,但要理解起来,可以分为3大阵营:sun官方(实力最弱)、Apache官方派 (实力中等)和 Ceki Gülcü实力派(实力高等)。
接下来我们就按时间顺序,看下它的历史发展历程。

Log4j
在JDK1.3及以前,Java打印日志依赖system.out.println(),system.err.println()或者e.printStackTrace(),Debug日志被写到STDOUT流,错误日志被写到STDERR流。这样打日志有一个非常大的缺陷,即无法定制化,且日志力度不够细。
于是,Ceki于2001年发布了Log4j,后来成为Apache基金会的顶级项目。Log4j在设计上非常优秀,对后续的Java Log框架有长久而深远的影响,它定义了L噢GG而、APPender、Level等概念如今已被广泛使用。Log4j的短板在于性能,在Logback 和 Log4j2出来以后,Log4j的使用也减少了。

J.U.L
受Log4j启发,Sun官方在Java1.4版本中引入了java.util.logging,但是j.u.l功能远不如Log4j完善,开发者需要自己编写Appenders(Sun称之为Handlers),且只有两个Handlers可用(Console和File),j.u.l在java1.5以后性能和可用性才有所提升。

JCL(commons-logging)
你方唱罢我登场,由于项目的日志打印必然选择两个框架中至少1个,这时候,Apache的JCL(commons-logging)诞生了。JCL是一个Log Facade,只提供Log API,不提供实现,然后有Adapter来使用Log4j或者JUL作为Log Implementation。
这时候一切看起来很简单,接口和实现做了良好的分离,在统一的JCL下,不改变任何代码,就可以通过配置就换用功能更强大,或者性能更好的日志库实现。不过commons-logging对Log4j和j.u.l的配置问题兼容的并不好,使用commons-logging还可能会遇到类加载问题,导致NoClassDefFoundError的错误出现。

SLF4J & Logback
SLF4J(Simple Logging Facad for Java)和Logback也是Ceki创立的项目(注意这时候他已经不在老东家Apache了,而是自己单干,难道天才的程序员都是无法忍受体质内生活?暂且不管),目的是为了提供更高性能的实现。
从设计模式的角度说,SLF4J是用来在log和代码层之间起到门面作用,类似于JCL的Log Facade。对于用户来说只要使用SLF4J提供的接口,即可隐藏日志的具体实现,SLF4J提供的核心API是一些接口和一个LoggerFactory的工厂类,用户只需要按照它提供的统一记录日志接口,最终日志的格式、记录级别、输出方式等可以通过具体日志系统的配置来实现,因此可以灵活地切换日志系统。
到这里,你可能会问:Apache已经有了JCL,用来做各种Log lib统一的接口,如果Ceki要搞一个更好的Log实现的话,直接写一个实现就好,为啥还要搞出一个SLF4J呢?
原因是Ceki认为JCL的API设计得不好,容易让使用者写出有性能问题的代码。
于是,现在有了2个流行的Log Facade(commons-logging和SLF4J),以及3个流行的Log Implementation。Ceki是个追求完美的人,他决定让这些Log之间都能够方便的替换,所以做了各种Adapter和Bridge来连接。

Log4j2
搞笑的一幕出现了,因为有了SLF4J和Logback,慢慢取代了JCL和Log4j,所以维护Log4j的人不愿坐视用户一点点被SLF4J/Logback吞噬,继而搞出了Log4j2(只能举个不恰当的例子,道高一尺,魔高一丈)。
Log4j2和Log4j1.x并不兼容,设计上很大程度模仿了SLF4J/Logback,性能也获得了很大提升。Log4j2也做了Facade/Implementation分离的设计,分成了log4j-api和log4j-core。
至此,形成了如今的“各霸一方”的场面。现在我们总结一下,如果按照设计角度,各日志框架的结构是这样的:
Java 日记框架_第1张图片
如果按照时间轴角度,各日志框架是这样的:
Java 日记框架_第2张图片

如何使用SLF4J

我们知道SLF4J是一个设计上很优秀的日志API,它底层必须绑定一个具体的日志实现,那它都兼容哪些日志实现呢?

API 绑定模块(适配器) 日志底层实现
slf4j-api.jar slf4j-log4j12-1.7.28.jar log4j.jar
slf4j-api.jar slf4j-jdk14-1.7.18.jar JDK1.4
slf4j-api.jar 原生实现,不需要适配器 logback-class-1.2.3.jar、logback-core-1.2.3.jar

如果需要切换底层日志实现,只需要调整适配器即可。比如需要把java.util.logging切换为log4j,只需要把slf4j-jdk14-1.7.28.jar替换为slf4j-log4j12-1.7.28.jar即可。下面这张图可以很好说明这个概念
Java 日记框架_第3张图片

如何兼容其他日志框架-桥接

假如我们在项目中使用了SLF4J作为日志API,Logback作为具体的日志实现(SLF4J+Logback天生一对,亲密好基友),按照上面的配置,只需要slf4j-api.jar、logback-class-1.2.3.jar、logback-core-1.2.3.jar这3个jar包即可,很完美。但是如果项目中引用的其他jar包是使用了不同的日志实现,比如log4j、JCL、java.util.logging,要想让这些包中的日志正常打印,还需要再引入log4j.jar,但这显然是不可能的,一个项目中只能有一个底层日志实现。所以,优秀的天才程序员Ceki设计了“桥接”这样的模块,把指向log4j,JCL 和 java.util.logging API的请求重定向到SLF4J API上,平滑过渡到你项目中指定的日志实现,看下图更好理解。
Java 日记框架_第4张图片
这个图需要注意右上角的情况,如果使用的SLF4J+Log4j的组合,也就是slf4j-api.jar、slf4j-log4j12.jar、log4j.jar的日志结构,如果需要桥接其他日志API,就不要再加log4j-over-slf4j.jar了,否则导致打印日志时会形成循环调用,最终java.lang.StackOverflowError

所以你的项目是SLF4J+Logback实现,为了处理兼容你引得包中其他日志实现的代码,引入jcl-over-slf4j.jar、log4j-over-slf4j.jar、jul-to-slf4j.jar即可,不用排包,可以看下这样的pom




    org.slf4j
    slf4j-api
    ${slf4j.version}



    org.slf4j
    log4j-over-slf4j
    ${slf4j.version}



    org.slf4j
    jcl-over-slf4j
    ${slf4j.version}



    org.slf4j
    jul-to-slf4j
    ${slf4j.version}



    ch.qos.logback
    logback-classic
    ${logback.version}


    ch.qos.logback
    logback-core
    ${logback.version}
    runtime


参考:
SLF4J官网配置手册
SLF4J官网桥接说明
Java常用日志框架介绍
Java日志体系

你可能感兴趣的:(JAVA基础)