官方原文https://www.slf4j.org/manual.html
简单Java日志外观(SLF4J)提供了不同的日志框架,例如java.util.logging、logback和log4j,的外观。SLF4J允许最终用户在部署时插入期望的日志框架。请注意SLF4J意味着你的库或者应用只有一个强制的依赖,即slf4j-api-1.7.12.jar。
从1.6.0版本开始,如果在类路径没有发现绑定(的日志框架),那么SLF4J将会默认无操作。
从1.7.0版本开始,在Logger接口中的打印方法提供可变参数的版本,即可以接受可变参数而不是Object[]数组。这个改变意味着SLF4J需要JDK 1.5以上的版本。在引擎盖的下面,Java编译器将方法中的可变参数转换为Object[]数组。这样,1.7.x编译器生成的Logger接口与1.6.x编译器生成的接口别无二致。因而断定,SLF4J 1.7.x版本完全无条件兼容1.6.x版本。
从1.7.5版本开始,在日志的检索次数上有很大的改善。考虑到改善的范围,强烈建立用户迁移到1.7.5以上的版本。
从1.7.9版本开始,通过设置slf4j.detectLoggerNameMismatch系统属性为true,SLF4J可以自动识别错误命名的logger。
延续编程领域的传统,这里用一个例子说明使用SLF4J最简单的方式输出"Hello world"。首先,获取一个HelloWorld类的logger日志对象。这个logger对象反过来用于记录"Hello World"消息。
import org.slf4j.Logger; |
为了运行这个例子,你首先需要下载slf4j的发布包,然后解压它。解压完后,将slf4j-api-1.7.12.jar添加到你的类路径中。
编译并运行HelloWorld,在控制台将会有输出如下的结果:
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
打印这个警告,是因为slf4j没有在类路径找到绑定(的日志框架)。
一旦你在类路径中添加了一个绑定(的日志框架),警告就会消失。假设你添加了slf4j-simple-1.7.12.jar,这样你的类路径中包含下面两个jar包:
编译并运行HelloWorld ,现在在控制台将输出如下的结果:
0 [main] INFO HelloWorld - Hello World
下面的示例代码说明了SLF4J一个典型的使用模式。注意第15行的占位符{}的使用。参考FAQ的问题"What is the fastest way of logging?"了解更多详细信息。
1: import org.slf4j.Logger; |
正如之前提到的,SLF4J支持不同的日志框架。SLF4J发布包附带的几个jar包被称为“SLF4J绑定”,每一个绑定对应一种支持的框架:
slf4j-log4j12-1.7.12.jar
绑定log4j 1.2版本,一个广泛使用的日志框架。也需要把log4j.jar包放到你的类路径中。
slf4j-jdk14-1.7.12.jar
绑定java.util.logging,也被称为JDK 1.4日志。
slf4j-nop-1.7.12.jar
绑定NOP无操作,默默丢弃所有的日志。
slf4j-simple-1.7.12.jar
绑定简单实现(的日志框架),这个简单的日志框架将所有的事件输出到System.err。只有INFO级别以上的消息才被打印。这个绑定在小型应用程序可能会很有用。
slf4j-jcl-1.7.12.jar
绑定了Jakarta Commons Logging日志框架。这个绑定委派所有的SLF4J日志到JCL日志框架中。
logback-classic-1.0.13.jar (requires logback-core-1.0.13.jar)
本地实现。有一些在SLF4J工程之外的与SLF4J绑定,例如logback本身就实现了SLF4J。
Logback的ch.qos.logback.classic.Logger类是SLF4J的org.slf4j.Logger接口的直接实现。因此,结合logback使用SLF4J节省很多内存和计算的开销。
切换日志框架,只需要在类路径中替换slf4j的绑定。例如,要从java.util.logging切换到log4j,只需要将slf4j-jdk14-1.7.12.jar替换为slf4j-log4j12-1.7.12.jar即可。
SLF4J不依赖于任何特殊的类加载工具。每一个SLF4J绑定都是在编译时就硬编码使用一个并且只使用一个日志框架。例如,slf4j-log4j12-1.7.12.jar绑定在编译时使用log4j绑定。在你的代码中,除了slf4j-api-1.7.12.jar包,你仅仅需要将一个且只有一个你选择的绑定丢到类路径中。不要在路径中放置1个以上的绑定。这里有个图来说明大概的意思。
PS:自己在项目中,使用的是logback实现,有一个同事在maven中添加dubbo的jar包时,而这个jar包使用的是log4j的实现,因为没有排除log4j的jar包,导致启动不起来,最后在控制台的提示中发现,slf4j绑定日志实现时混乱了,因为出现了两个绑定,最后在pom中排除掉相应的log4j即可
org.apache.zookeeper
zookeeper
3.4.10
org.slf4j
slf4j-log4j12
log4j
log4j
SLF4J的接口和它们不同的适配器是极其简单的。大多数熟悉Java语言的开发人员都应该能够在一个小时内读懂并完全理解这些代码。不需要知道类加载器的知识,SLF4J没有使用也没有直接访问任何类加载器。因而,SLF4J没有类加载器的问题或者在JCL中的内存泄漏。
考虑到SLF4J接口和部署的简单性,新的日志框架的开发者应该觉得编写SLF4J绑定是很容易的。
为了避免将日志框架强加给最终用户,组件和库的作者可能编码时紧靠SLF4J接口。这样,最终用户在部署时可能选择期望的日志框架,只需在类路径中插入相应的slf4j绑定即可。这个可能会发生改变的,要用另外一个绑定提换已经存在的绑定,并重启应用程序。这个方法证明是非常简单和稳定的。
从SLF4J 1.6.0版本开始,如果没有在类路径中发现绑定,那么slf4j-api将会使用默认的无操作实现,丢弃所有的日志请求。这样,不会因为org.slf4j.impl.StaticLoggerBinder类丢失了抛出NoClassDefFoundError异常,SLF4J 1.6.0以上版本会发出一个绑定缺失的告警消息,继续丢弃所有的日志请求。例如,假设Wombat是与生物有关的框架,依赖SLF4J记录日志。为了避免强加一个日志框架给最终用户,Wombat的发布包包含了slf4j-api.jar包,但是没有绑定(特定的日志框架)。即使类路径中缺失了SLF4J绑定,Wombat的发布包仍然是即开即用的,不需要最终用户从SLF4J的网站上下载绑定。只有当最终用户决定使用日志了,他才需要安装与选择的日志框架对应的SLF4J绑定。
基本规则:嵌入式的组件例如库或者框架不应该声明依赖于任何SLF4J的绑定,而是依赖于slf4j-api。当库声明了一个传递依赖于特定的绑定,绑定被强加给最终用户了,这是与SLF4J的目的相悖的。记住,声明非传递性依赖于绑定,例如测试,不要影响最终用户。
在嵌入式组件中使用SLF4J在FAQ中讨论了,有关的问题是日志配置,减少依赖和测试
考虑到Maven的传递依赖规则,对于普通工程(不是库或者框架)声明日志依赖可以通过单一的依赖声明来完成。
logback-classic:如果你想要使用logback-classic作为日志框架,你需要在pom.xml文件中声明"ch.qos.logback:logback-classic"依赖。除了logback-classic-1.0.13.jar包,这会将slf4j-api-1.7.12.jar包连同logback-core-1.0.13.jar包加入工程中。记住明确地声明依赖logback-core-1.0.13或者slf4j-api-1.7.12.jar是没有错的,有时可能是必要的。例如,根据Maven就近依赖原则,为了强加一个正确的版本给上述的artifact,就需要明确地声明。如下所示:
log4j:如果你想要使用log4j作为日志框架,你需要在pom.xml文件中声明"org.slf4j:slf4j-log4j12"作为依赖。除了slf4j-log4j12-1.7.12.jar,这会将slf4j-api-1.7.12.jar包连同log4j-1.2.17.jar包加入工程中。记住明确地声明log4j-1.2.17.jar或者slf4j-api-1.7.12.jar依赖,有时可能是必要的。例如,根据Maven就近依赖原则,为了强加一个正确的版本给上述的artifact,就需要明确地声明。如下所示:
Java.util.logging:如果你想要使用java.util.logging作为日志框架,你需要在pom.xml文件中声明"org.slf4j:slf4j-jdk14"作为依赖。除了slf4j-jdk14-1.7.12.jar,这会将slf4j-api-1.7.12.jar包连同log4j-1.2.17.jar包加入工程中。记住明确地声明slf4j-api-1.7.12.jar依赖,有时可能是必要的。例如,根据Maven就近依赖原则,为了强加一个正确的版本给上述的artifact,就需要明确地声明。如下所示:
SLF4J绑定指派一个工件,例如slf4j-jdk14.jar包或slf4j-log4j12.jar包用于将slf4j绑定到一个日志框架,分别是java.util.logging和log4j。
从客户端的角度看,所有版本的slf4j-api 都是兼容的。对于任意的N和M,用slf4j-api-N.jar编译的客户端代码用slf4j-api-M.jar运行也非常好。只需要确保绑定的版本与slf4j-api.jar匹配。你不必担心你的工程中依赖所使用的slf4j-api.jar的版本。
slf4j-api.jar版本和SLF4J绑定的版本混合使用可能会引发问题。例如,如果你使用slf4j-api-1.7.12.jar包,那么你应该也使用slf4j-simple-1.7.12.jar包,使用slf4j-simple-1.5.5.jar将无法工作。
然而,从客户端的角度看,所有的slf4j-api的版本都是兼容的。对于任意的N和M,用slf4j-api-N.jar编译的客户端代码如果用slf4j-api-M.jar运行也没问题。只需要确保绑定的版本与slf4j-api.jar相匹配。你不必担心你的工程中的依赖所使用的slf4j-api.jar的版本。你总是可以使用slf4j-api.jar的任意版本,只要slf4j-api.jar的版本和它的绑定版本相匹配,就可以工作的很好。
在初始化时,如果SLF4J怀疑slf4j-api的版本和绑定的版本不匹配,它会发出一个怀疑版本不匹配的告警。
大多数的时候,一个工程会依赖不同的组件,而这些组件又依赖的是日志API,而不是SLF4J。你会发现一个普遍的问题,很多工程同时依赖了JCL、java.util.logging、log4j和SLF4J。然后就很期望通过一个单一的途径整合日志。SLF4J通过为JCL、java.util.logging、log4j提供桥模块满足了这个普遍的使用场景。了解更多详细的信息,请参考“桥接传统API”。
映射诊断上下文(Mapped Diagnostic Context)本质上是由日志框架维护的一个映射表map。在映射表中,应用程序提供了键值对,这些键值对可以被掺入到日志框架的日志消息中。MDC数据在过滤消息和触发特定的工作上也是很有用的。
SLF4J支持MDC。如果底层的日志框架支持MDC功能,那么SLF4J将代理底层框架的MDC。记住,这时只有log4j和logback提供了MDC功能。如果底层的日志框架不支持MDC,例如java.util.logging,那么SLF4J仍然存储MDC数据,但是其中的额信息需要由自定义的用户代码获取。
这样,作为SLF4J用户,你可以充分发挥在log4j和logback中存在的MDC信息的优势,但是不要强制这些日志框架依赖于用户。
关于MDC更多的信息,请参看logback手册的关于MDC的章节。
优点 |
描述 |
在部署时选择日志框架 |
通过在类路径中插入合适的jar(绑定),日志框架可以在部署时插入 |
Fail-fast机制 |
由于JVM加载类的方式,框架绑定会在很早的时候自动验证。如果SLF4J在类路径中找不到绑定,它就会发出一个单一的告警消息。默认情况下,绑定是没有操作实现NOP。 |
提供流行的日志框架的绑定 |
SLF4J支持流行的日志框架,即log4j、java.util.logging、simple logging和NOP。logback工程原生支持SLF4J |
桥接传统日志API |
在SLF4J之上实现JCL,即jcl-over-slf4j.jar,将允许你的工程迁移到SLF4J,而不会破坏使用JCL的已有软件的兼容性。类似的,log4j-over-slf4j.jar和jul-to-slf4j模块将分别允许你将log4j和java.util.logging重定向到SLF4J。参考桥接传统API以了解更多详细信息。 |
迁移源代码 |
Slf4j迁移器实用工具可以帮助你把代码迁移到使用SLF4J |
支持参数化的日志消息 |
所有的SLF4J绑定支持参数化的日志消息,大大提高了性能。 |