日志是Java程序员日常开发当中必须要接触的一个环节。Java的日志五花八门,有各种各样的实现,现在梳理一下Java日志相关的框架,及其使用使用细节。我们在日常使用过程中,主要会接触到SLF4J、Logback、Log4j1、Log4j2、JCL和J.U.L等日志框架。
对于日志框架的最佳实践,推荐使用使用Log Facade,而不是具体Log Implementation,即在代码中直接使用 Log4j-API 或者 SLF4J(不推荐继续使用 JCL)的类,而不推荐使用Log4j1,Log4j2,Logback或者J.U.L的类(配置文件当中配置好即可)。
Log4j-API/Log4j-core类似于SLF4J/Logback,在Log4j2的官方文档当中宣称比Logback性能高很多LINK,其实现方案类似于SLF4J/Logback,因此,在这里主要分析SLF4J与不同的日志框架的关联使用情况。
1. SLF4J承接不同的日志框架(日志框架—->SLF4J)
当项目是使用多种日志API时,可以统一适配到SLF4J,中间使用SLF4J或者第三方提供的日志适配器适配到SLF4J,SLF4J再底层用开发者想用的一个日志框架来进行日志系统的实现,从而达到了多种日志的统一实现。其中的技术实现大体有一下两种方式:
- 重写上游类的实现。比如jcl-over-slf4j和log4j-over-slf4j,为了承接log4j 1,重写了log4j 1 的Logger和LogFactory类。
- 对接上游类的扩展方案。比如jul-to-slf4j和log4j-to-slf4j,为了承接J.U.L,实现了继承java.util.logging.Handler的SLF4JBridgeHandler。
适配器名称 | 原日框架 | 提供方 | 删除的依赖 | 实现方式 | 备注 |
---|---|---|---|---|---|
jcl-over-slf4j | apache commons-logging | SLF4J | API:删除commons-logging 实现:删除非SLF4J实现的实现类,如SLF4J采用Logback的实现,那么就需要删除Log4j1等实现 | jcl-over-slf4j重写了commons-logging的Log和LogFactory类,做了不同的实现 | JCL 是一个Log Facade,只提供 Log API,不提供实现,然后有 Adapter 来使用 Log4j 或者 JUL 作为Log Implementation,是Ceki Gulcu 认为 JCL 的 API 设计得不好,容易让使用者写出性能有问题的代码,所以重新设计了SLF4J替代JCL。 |
jul-to-slf4j | java jdk-logging | SLF4J | API:删除非SLF4J之外的其他Log Facade | jul-to-slf4j 下有 SLF4JBridgeHandler实现,系统启动的时候调用SLF4JBridgeHandler.removeHandlersForRootLogger();删除所有的Logger,然后调用SLF4JBridgeHandler.install();装载上SLF4J | JUL是JDK自带的log功能,虽然是官方自带的log lib,但是由于性能问题和功能等问题,使用不广泛 |
log4j-over-slf4j | apache log4j 1 | SLF4J | API:删除非SLF4J之外的其他Log Facade 实现:删除非SLF4J实现的实现类,如SLF4J采用Logback的实现,那么就需要删除Log4j1等实现 | log4j-over-slf4j 重写了log4j 1 的Logger和LogFactory类,做了不同的实现 | Log4j 在设计上非常优秀,对后续的 Java Log 框架有长久而深远的影响。Log4j 的短板在于性能,在Logback 和 Log4j2 出来之后,Log4j的使用也减少了 |
log4j-to-slf4j | apache Log4j 2 | Log4j 2 | API:删除非SLF4J之外的其他Log Facade 实现:删除非SLF4J实现的实现类,如SLF4J采用Logback的实现,那么就需要删除Log4j2等实现 | log4j-to-slf4j 使用OSGI SPI的形式为org.apache.logging.log4j.spi.Provider提供了SLF4J的实现 | Log4j 2的性能很好,是未来之星!官方文档 |
2. SLF4J对接不同的日志框架实现(SLF4J—->日志框架)
SLF4J适配不同的日志实现,是通过不同的适配器实现的。
- 在1.8.0-alpha0以前版本采用实现org.slf4j.impl.StaticLoggerBinder的方式实现,
- 在1.8.0-alpha0之后的版本采用提供org.slf4j.spi.SLF4JServiceProvider的SPI实现的方式实现。
显然,第一中方式不够优雅,所以Ceki Gulcu在2017年3月21日对SFL4J的提交过程当中删除org.slf4j.impl.StaticLoggerBinder,增加了org.slf4j.spi.SLF4JServiceProvider,将原来的查找实现的方式变成了查找SPI服务的方式,但是最新的SPI方案并没有做对久的org.slf4j.impl.StaticLoggerBinder方式的兼容,这个动作欠妥的,比如SLF4J与Logback的配合上就有问题,类似的问题会出现在其他日志实现当中。
SLF4J 1.8.0以前版本 | SLF4J 1.8.0以后版本 |
---|---|
Logback 1.3.0以前版本 | 正常使用 |
Logback 1.3.0以后版本 | 无法正常使用 |
log4j-slf4j-impl是的Log4j 2提供的对SLF4J的实现,由于是第三方提供的方案,所以其采用了实现org.slf4j.impl.StaticLoggerBinder的方案进行扩展。
2.1 适配器名称说明
适配器名称 | 目标实现 | 提供方 | 备注 |
---|---|---|---|
slf4j-jdk14 | jdk-logging J.U.L | SLF4J | |
logback-classic | Logback | Logback | Logback与SLF4J都是Ceki Gulcu的作品,Logback默认带SLF4J的适配 |
slf4j-jcl | apache commons-logging | SLF4J | |
slf4j-log4j12 | Log4j 1 | SLF4J | |
log4j-slf4j-impl | Log4j 2 | Log4j 2 | 官方文档 |