门面之下,日志之上-SLF4J
您目前处于:编程  2017年01月22日

每一位程序员自从屏幕输出的第一条"Hello World"那一刻开始,便从此与日志结下了不解之缘。

SLF4J框架的定义

Java 简易日志门面(Simple Logging Facade for Java,缩写SLF4J),是一套包装 Logging 框架的界面程式,以外观模式(门面模式)实现。可以在软件部署的时候决定要使用的 Logging 框架,目前主要支援的有 Java Logging API、log4j 及 logback 等框架。以 MIT 授权方式发布。

SLF4J 的作者就是 log4j 的作者 Ceki Gülcü,他宣称 SLF4J 比 log4j 更有效率,而且比 Apache Commons Logging (JCL) 简单、稳定。

与log4j对比

我们最长用的日志实现框架莫过于 log4j 了,现在来简单对比下 log4j 和 slfj,其实他们俩实质上是两个不同的东西,我们来对比使用上的区别。

1. log4j 提供 TRACE,DEBUG,INFO,WARN,ERROR 及 FATAL 六种纪录等级,但是 SLF4J 认为 ERROR 与 FATAL 并没有实质上的差别,所以拿掉了 FATAL 等级,只剩下其他五种。

2. log4j 间接的在鼓励程序员使用 string 相加的写法,而 slf4j 就不会有这个问题,你可以使用:

logger.error("{} is userid",userid);

3. 使用 slf4j 可以方便的使用其提供的各种集体的实现的 jar。(类似commons-logger)下面会着重介绍。

4. 提供字串内容替换的功能,会比较有效率,说明如下

// 传统的字符串产生方式,如果没有要记录 Debug 等级的信息,就会浪费时间在产生不必要的信息上 
logger.debug("There are now " + count + " user accounts: " + userAccountList); 

// 为了避免上述问题,我们可以先检查是不是开启了 Debug 信息记录功能,只是程序的编码会比较复杂 
if (logger.isDebugEnabled()) { 
    logger.debug("There are now " + count + " user accounts: " + userAccountList); 
} 

// 如果 Debug 等级没有开启,则不会产生不必要的字符串,同时也能保持程序编码的简洁
logger.debug("There are now {} user accounts: {}", count, userAccountList);

SLF4J整体架构介绍说明,先来看两幅图

SLF4J第一幅图:

第一层,1- 应用层,没什么说的,就是我们各自的应用。

第二层,2- slf4j 提供的 api,统一依赖 slf4j-api.jar,这一层之下,就是自由层了,可以看到几乎包括了所有的我们熟悉的日志实现。

第三层,3- 这是两个桥接器或者是适配器,通过名字就可以看出来是,对应 log4j 和 jdk 自带的日志。左边的 slf4j-log4j12.jar 桥接器看名字就知道是 slf4j 到 log4j 的桥接器,右边的 slf4j-jdk14.jar 就是 slf4j 到 Java 原生日志实现的桥接器了。他们俩之下,第四层,也就是具体的实现。4- 这三个不需要桥接器,logback 自己实现了slf4j,据说 logback 的作者跟 slf4j 是同一个人。右边的两个是 slf4j 自带的本身实现。

第四层,5- 分别是 Apache 的 log4j 和 jdk 本身的 java.util.logging.Logger 实现,jdk 环境里面包含了这个类,因此也不需要特别引入。

SLF4J第二幅图:

这张图和第一幅图,是相反的,也就是,将其他日志的 api 转调到 slf4j 上面,举个例子,比如我们的应用系统里面目前使用了 log4j,这个时候我们想升级日志,不用 log4j了,想采用 logback,就有一种办法了,就是我们将 log4j 转调到 slf4j 上面,通过 slf4j 的 api 调用到 logback 上去。看下面这个场景:

场景介绍:如使用 log4j1 的 API 进行编程,但是想最终通过 logback 来进行输出,所以就需要先将 log4j1 的日志输出转交给 slf4j 来输出,slf4j 再交给 logback 来输出。将 log4j1 的输出转给 slf4j,这就是 log4j-over-slf4j 做的事。

这里面还有一个,要说的是,JCL(commons-logging)其实也是一种日志外观,而且 commons-logging + log4j 也是经典的一个日志实现方案,像我们常用的框架,spring、ibatis 都是采用的这种框架,但是 SLF4J 在设计上更简单和更健壮。简而言之,SLF4J 避免了植入 JCL 的类加载问题(commons-logging 是通过动态加载的机制,在程序运行时自动查找真正的日志实现,使用了 ClassLoader 加载底层日志类库,有时候导致了 OSGI 这样的框架无法正常工作。slf4j 是采用编译时静态绑定,因此可以在 OSGI 中使用)。基于 commons-logging + log4j 这样的实现,通过第二幅图我们知道,也是可以转接到 SLF4J 进而采用 slf4j 桥接之下的任意日志输出,比如1里面的将 jcl 转接到 slf4j 进而通过 logback 输出日志。

这里可以看到,几乎所有的日志实现都可以转调到 slf4j 上面,但是要注意,转调回 slf4j 的日志框架比如 log4j-over-slf4j,不能跟 slf4j 当前桥接到的日志框架比如 slf4j-log4j12,同时出现在 classpath 下面。

当前 slf4j 最新的框架版本是 1.7.22,我们从官网下载下来以后,包含的文件如下,可以结合上面两幅图来理解,就不难掌握了。

到此我们对 SLF4J 有了一个基本的认知。SLF4J 不同于其它的日志类库,而且与其它的类库有很大的不同。SLF4J(Simple loggin Facade for Java)并不是一个真正的日志实现,而是一个抽象层,这样我们就可以在我们的应用里面使用我们想使用的日志类库。可以看出来,如果我们编写的是 API 或者是通用的类库,这些 API 或者类库要拿出去给其他组织或团队使用,那么我们采用 SLF4J 就是非常好的一种方式。

SLF4J静态绑定原理

SLF4J 和 Apache Common Logging 不同,SLF4J 采用的是静态绑定,静态绑定的意思就是为每一个具体的日志库写一个,绑定类 org.slf4j.impl.StaticLoggerBinder,这个类会存放在我们引入的桥接类(适配器类)或者是 slf4j 自身的实现类里面。比如 slf4j-log12.jar,如图:

源文件:

SLF4j 便会装载 (load) 对应版本的 org.slf4j.impl.StaticLoggerBinder,从而调用具体的 log4j 日志库。

总结一下:

SLF4J 不同于其它的日志类库,而且与其它的类库有很大的不同。SLF4J (Simple loggin Facade for Java) 并不是一个真正的日志实现,而是一个抽象层,这样我们就可以在我们的应用里面使用我们想使用的日志类库。可以看出来,如果我们编写的是API或者是通用的类库,这些API或者类库要拿出去给其他组织或团队使用,那么我们采用SLF4J就是非常好的一种方式。


Reference:

https://my.oschina.net/pingpangkuangmo/blog/410224

http://blog.csdn.net/kxcfzyk/article/details/38613861

https://www.slf4j.org/

https://zh.wikipedia.org/wiki/SLF4J


转载请并标注: “本文转载自 linkedkeeper.com (文/王新栋)”