《SLF4J官方文档》SLF4J-FAQ 常见问题解答(一)
一般性问题
- 什么是SLF4J?
- 什么时候应该使用SLF4J?
- SLF4J仍是另一个日志外观吗?
- 如果SLF4J可修复JCL,那为什么不在JCL里加入修复而是创建一个新项目?
- 使用SLF4J时,我必须重新编译我的应用以转换到一个不同的日志系统吗?
- SLF4J的要求是什么?
- SLF4J向后兼容版本吗?
- 使用SLF4J时遇到访问权限错误,原因是什么?
- 为什么SLF4J是在X11类型许可证下许可而不是Apache软件许可?
10.在哪里能获得特定的SLF4J绑定?
11.我的库应该尝试配置logging吗?
12.为了减小我们软件的依赖库数量,我们想让SLF4J成为一个可选择的依赖包。这个是好主意吗?
13.Maven传递依赖怎么样?
14.如何排除依赖Maven的commons-logging?
关于SLF4J API
- 为什么日志器接口的打印方法不接收对象类型的消息,但只收String类型消息?
- 我可以输出一个没有消息的异常日志吗?
- 输出日志(关闭日志)的最快方法是什么?
- 如何打印单独(可能是复杂的)对象的字符串内容日志?
- 为什么slf4j.Logger接口没有FATAL级别的方法?
- 为什么TRACED级别的日志只在SLF4J 1.4.0版本介绍?
- SLF4J日志API支持I18N(国际化)吗?
- 不通过LoggerFactory里的静态方法,可以重新获取日志器吗?
- 存在错误/抛出异常时,可以参数化日志声明吗?
实现SLF4J API
- 如何使我的日志底层SLF4J可兼容?
- 如何使我的日志系统添加支持Marker接口?
- SLF4J的版本检查机制如何工作?
关于日志的一般性问题
1.类的日志器成员变量应该声明为静态变量吗?
2.类里的声明日志器有推荐的习语吗?
一般性问题
1.什么是SLF4J?
SLF4J是一个简单的日志系统外观,它允许终端用户在部署时植入所需日志系统。
2.什么时候应该使用SLF4J?
简单而言,为库和其他嵌入组件的日志需求,它们应该考虑下SLF4J,因为库不能强加终端用户日志框架的选择。另外,独立应用使用SLF4J不一定有意义。独立应用可以直接援引他们选择的日志框架。对于logback,问题是没有实际意义的,因为logback通过SLF4J暴露它的logger API。
SLF4J只是个外观,意味着它不提供一个完整的日志方案。例如配置输出源或者设置日志级别的操作不能和SLF4J执行。因此,在某个时间点上,任何微不足道的应用将需要直接援引底层日志系统。换句话说,完全对立的API底层日志系统是不可能给独立应用程序使用的。然而,SLF4J减少这种依赖的影响到无痛苦级别。
假设你的CRM应用用log4j来记录日志。然后,你的一个重要客户端请求日志要通过JDK1.4执行日志记录。如果你的应用充斥着数以千计的直接log4j调用,转移到JDK1.4的调用将相对是一个冗长和容易出错的过程。更糟的是,你可能需要维护两个版本的CRM软件。你已经引用SLF4J API而不失log4J,通过替换二者的jar文件,迁移可在几分钟内完成。
SLF4J允许组件开发人员推迟终端用户的日志系统的选择,但最终仍需要做出一个选择。
3.SLF4J仍是另一个日志外观吗?
SLF4J在概念上和JCL非常相似。因此它可以被认为是另一个日志外观。但是,SLF4J在设计上更简单和更健壮。简而言之,SLF4J避免了植入JCL的类加载问题。
4.如果SLF4J可修复JCL,那为什么不在JCL里加入修复而是创建一个新项目?
这是非常好的问题。首先,SLF4J静态绑定方法非常简单,甚至很可笑。很难说服开发者这个方法的有效性。仅仅在SLF4J发布后和开始变得被接收后,它才在相关社区里获得尊重。
其次,SLF4J提出2个改进手段,这都趋向被低估了。用务实的方法,参数化日志消息解决了一个跟日志性能联系的重要问题。标记对象,由org.slf4j.Logger接口支持,为高级日志系统的采用铺路,如果必要的话,同时让放开转回更多传统日志系统的大门。
5.使用SLF4J时,我必须重新编译我的应用以转换到一个不同的日志系统吗?
不,你不需要重新编译你的应用。你可以通过移除之前的SLF4J绑定,然后将它替换为你选择的绑定,就能转换到不同的日志系统了。
例如,如果你在使用NOP实现,希望转换到log4j 1.2版本,在你的类目录下用 slf4j-log4j12.jar 替换slf4j-nop.jar,但也别忘了添加log4j-1.2.x.jar 。像转换到JDK 1.4 logging?只需用slf4j-jdk14.jar替换slf4j-log4j12.jar。
6.SLF4J的要求是什么?
1.7.0版本SLF4J需要JDK1.5以上。早期的SLF4J版本,像SLF4J 1.4,1.5和1.6,要求JDK1.4。
Binding | Requirements |
slf4j-nop | JDK 1.5 |
slf4j-simple | JDK 1.5 |
slf4j-log4j12 | JDK 1.5, plus any other library dependencies required by the log4j appenders in use加入任何其他所需的依赖包取决于使用的log4j输出源 |
slf4j-jdk14 | JDK 1.5 or later |
logback-classic | JDK 1.5 or later, plus any other library dependencies required by the logbackappenders in use加入任何其他所需的依赖包取决于使用的log4j输出源 |
7.SLF4J向后兼容版本吗?
从客户端角度,SLF4J API对所有版本都向后兼容。这意味着你能从SLF4J版本1.0升级到任何以后的版本,没有任何问题。对于slf4j-api版本N和slf4j-api
版本M, 版本N编译的代码将和版本M编译的代码一起正常运作。迄今为止, slf4j-api里的二进制兼容性从来没有被打破。
然而,从客户端角度,SLF4J API非常稳定时,SLF4J绑定,比如slf4j-simple.jar
或slf4j-log4j12.jar,需要一个特定版本的SLF4J-API。混合不同版本的slf4j可能会出现问题和强烈不提倡的。例如,如果你正在使用slf4j-api-1.5.6.jar,然后你也应该使用slf4j-simple-1.5.6.jar,使用slf4j-simple-1.4.2.jar将不会工作。
在初始化时时,如果SLF4J推测可能有版本不匹配问题,它将发出一个关于不匹配的警告。
8.使用SLF4J时遇到访问权限错误,原因是什么?
如下是错误详情:
1 |
Exception in thread "main" java.lang.IllegalAccessError: tried to access field
|
2 |
org.slf4j.impl.StaticLoggerBinder.SINGLETON from class
|
3 |
org.slf4j.LoggerFactory at |
4 |
org.slf4j.LoggerFactory.<clinit>(LoggerFactory.java: 60 )
|
这个问题是由LoggerFactory类的静态初始化尝试直接访问org.slf4j.impl.StaticLoggerBinder的单例变量造成的。这在SLF4J 1.5.5和之前的版本允许,在1.5.6及以后的版本,单例变量已经被标记为私有访问权限了。
如果你遇到了上述的错误,你可以用老版本的slf4j-api,比如1.4.3,同时使用一个新版本的slf4j绑定,比如1.5.6。通常,当你的Maven pom.xml文件使用hibernate 3.3.0,在slf4j-api 1.4.2版本上声明一个依赖时,将发生上述异常。如果你的pom.xml在slf4j绑定上声明依赖,比方说slf4j-log4j12 版本1.5.6,你会触发非法访问错误。
查看Maven里那个slf4j-api版本被拉入,用下述maven依赖植入。
1 |
mvndependency:tree |
如果你使用Eclipse,请不要依靠m2eclipse显示的关系树。
如果你的pom.xml明确在slf4j-api上声明了依赖,这个版本的api匹配已声明的绑定,将使这个问题消失。
也请读向后兼容性的FAQ,以获得更多普遍性解释。
9.为什么SLF4J是在X11类型许可证下许可而不是Apache软件许可?
SLF4J是在X11类型许可证下许可而不是Apache软件许可,这是因为X11许可是被Apache软件基金会以及自由软件基金会的各自许可认为是科兼容的。
10.在哪里能获得特定的SLF4J绑定?
给SimpleLogger、NOPLogger、og4jLoggerAdapter和JDK14LoggerAdapter 的SLF4J绑定包含在slf4j-nop.jar, slf4j-simple.jar,slf4j-log4j12.jar, and slf4j-jdk14.jar文件里。
给logback-classic的绑定附带在logback distribution(https://logback.qos.ch/download.html)上。对于其他所有的绑定,logback-classice绑定需要slf4j-api.jar。
11.我的库应该尝试配置logging吗?
嵌入的组件,比如库,不进不需要配置底层日志框架,它们也不应该做这些。它们应该引入SLF4j来记录日志,但也应该让终端用户配置日志环境。当嵌入的组件尝试在自己配置日志时,它们常常会覆盖终端用户的意愿。在一天的结束时,终端用户不得不读日志,处理日志。她应该是决定自己想要的日志配置的人。
12.为了减小我们软件的依赖库数量,我们想让SLF4J成为一个可选择的依赖包。这个是好主意吗?
当一个软件工程到达它需要设计日志策略的点时,这个问题会提起 。
让Wombat成为一个具有极少依赖的软件库。如果SLF4J被选作为Wombat的日志API,那么一个新的slf4j-api.jar依赖库将被加入带Wombat的依赖包列表中。由于写日志的包装并不难,一些开发者冒险包装SLF4J,只有当它已经出现在类路径中时链接它,使得SLF4J成为Wombat的一个可选择的依赖库。为了解决这个依赖问题,包装将被从SLF4J的API中隔离Wombat,以确保Wombat中的日志不会过时的。
另外,任何SLF4J-包装都取决于SLF4J得定义。它们一定有相同的通用api。在将来,如果出现一个新的、明显不同的日志API,和直接使用SLF4J的代码一样,使用包装器的代码也将同样很难移植到新的API.因此,包装器不可能是面向未来的代码,但在SLF4J上增加了一个额外的间接连接,使得这更复杂,这本身就是一中间接连接。
脆弱性增加实际上比这个还遭。包装器需要依赖随时变化的一些内部SLF4J接口,这违背了面向客户端API永不改变的原则。因此,包装器通常依赖一些同他们一起编译的主要版本。针对SLF4J 1.5.x版本编译的包装器将不会同SLF4J 版本1.6工作,但是使用org.slf4j.Logger, LoggerFactory, MarkerFactory, org.slf4j.Marker和MDC的客户端代码将和任何版本SLF4J 1.0及以后的版本正常工作。合理地假设在大部分工程中,Wombat将是许多依赖库中的一个。如果每个依赖库有自己日志包装器,那么大概每个包装器需要独立配置。因此,不只是处理SLF4J的日志框架,Wombat的用户也必须处理Wombat的日志包装器。
为了使SLF4J可选择,每个框架提出的自己的包装器将加剧这个问题。(配置或处理5个不同的日志包装器将很不愉快,也不招人喜欢)
Velocity项目采纳的日志策略 是一个“自定义日志抽象”反模式的好例子。通过采用一个独立的日志抽象的策略,Velocity开发者已经使自己的开发更复杂,但更重要的是,他们让开发对他们的用户更难。
一些项目试着在类路劲上检测SLF4J的存在性,如果存在的话就转换到SLF4J.虽然这个方法看上去足够的透明,但是它将导致错误的位置信息。底层日志框架将打印包装器的位置(类名和行数)而不是打印真正调用者的位置。还有就是除了参数化日志、像SLF4J支持MDC和标记的API覆盖问题。尽管有人可以在数小时内提出一个似乎可运行的SLF4J包装器,随着时间的推移,许多技术问题将出现,这是Wombat开发者不得不处理的问题。注意SLF4J已经发展了好几年了,有260个bug报告提起它。
基于以上原因,框架的开发者应该抵制自己写的日志包装的诱惑。 它不仅是浪费开发者时间,对上述日志的用户,这实际上让开发更困难,使日志代码自相矛盾地更容易改变。
13.Maven传递依赖怎么样?
作为使用Maven构建库作者,你可能想使用绑定来测试你的应用程序,比如SLF4J-log4j12或的logback经典,对用户不强制log4j或logback-classic作为依赖。 这是比较容易做到的。
基于你的库的代码依赖于SLF4J API,你需要声明SLF4J的API作为一个编译时(默认范围)的依赖。
1 |
<dependency> |
2 |
<groupId>org.slf4j</groupId>
|
3 |
<artifactId>slf4j-api</artifactId>
|
4 |
<version> 1.7 . 21 </version>
|
5 |
</dependency> |
通过声明SLF4J绑定依赖为“测试”的范围,可以实现限制在测试绑定使用的SLF4J的传递。例如:
1 |
<dependency> |
2 |
<groupId>org.slf4j</groupId>
|
3 |
<artifactId>slf4j-log4j12</artifactId>
|
4 |
<version> 1.7 . 21 </version>
|
5 |
<scope>test</scope>
|
6 |
</dependency> |
因此,至于你的用户要导出SLF4J的API作为库的传递依赖,但不是任何SLF4J绑定或任何底层日志系统。
需要注意的是由于SLF4J 1.6版,在没有SLF4J绑定,SLF4J的API将默认为无无操作实现。
14.如何排除依赖Maven的commons-logging?
替代1)明确排除
很多使用Maven的软件项目声明commons-logging作为一个依赖。因此,如果你希望迁移到SLF4J或使用jcl-over-slf4j,你需要排除项目中所有依赖里的comms-logging,这些依赖库传递依赖common-logging。依赖排除 在Maven文件里已有描述。为分布在几个pom.xml里的多个依赖明确地排除common-logging,可能是一个笨重的和相对容易出错的过程。
替代2)规定的范围
在项目的pom.xml文件中所提供的范围内,通过声明Commons-logging,它可以像依赖一样简单、方便地排除。实际的commons-logging类将由jcl-over-slf4j提供。 这转化为以下POM文件片段:
01 |
<dependency> |
02 |
<groupId>commons-logging</groupId>
|
03 |
<artifactId>commons-logging</artifactId>
|
04 |
<version> 1.1 . 1 </version>
|
05 |
<scope>provided</scope>
|
06 |
</dependency> |
07 |
08 |
<dependency> |
09 |
<groupId>org.slf4j</groupId>
|
10 |
<artifactId>jcl-over-slf4j</artifactId>
|
11 |
<version> 1.7 . 21 </version>
|
12 |
</dependency>
|
第一依赖性声明本质规定commons-logging会通过你的环境“以某种方式”提供。第二个声明引入jcl-over-slf4j到你的项目中。 作为jcl-over-slf4j是commons-logging完美的二进制兼容更换,第一个断言为真。
不幸的是,虽然在规定范围内声明的commons-logging能够完成任务,你的IDE,比如Eclipse中,仍将在项目的类路径上放置commons-logging.jar,通过你的IDE可以看到。 您将需要确保jcl-over-slf4j前,您的IDE 的commons-logging.jar的是可见的。
最后更新:2017-05-19 16:01:58