这是我人生中第一次发现自己正在编写一个开源的Java API。希望能被包括在其他项目中。
对于日志,我(以及与我一起工作的人)一直使用JUL (java.util.logging
),并且从未遇到过任何问题。然而,现在我需要更详细地了解我应该为我的API开发做什么。我对此做了一些研究,根据我得到的信息,我只是更加困惑。所以才有了这篇文章。
因为我来自JUL,所以我对此有偏见。我对其他的知识就没那么多了。
从我所做的研究中,我得出了人们不喜欢JUL的这些原因:
所以真正的大问题是为什么不是JUL?。我错过了什么?关于日志facade (SLF4J, JCL)的原因是历史上已经存在了多种日志实现,其原因实际上可以追溯到JUL之前的时代。如果JUL是完美的,那么日志facade就不存在了。更让人困惑的是,JUL在某种程度上是一个门面,它允许处理程序、格式化器甚至LogManager交换。
与其采用多种方式来做同一件事(日志),我们难道不应该质疑为什么它们是必需的吗?(看看这些原因是否还存在)
好的,到目前为止,我的研究已经导致了一些事情,我可以看到可能是真正的问题与JUL:
log.finest("Lookup request from username=" + username
+ ", valueX=" + valueX
+ ", valueY=" + valueY));
我的IDE会警告我并请求允许它将其更改为:
log.log(Level.FINEST, "Lookup request from username={0}, valueX={1}, valueY={2}",
new Object[]{username, valueX, valueY});
.. 我当然会接受批准!谢谢你的帮助。
所以我自己不写这样的语句,这是由IDE完成的。
总之,关于表现的问题,我没有发现任何迹象表明JUL的表现与竞争对手相比不佳。
我很担心我忽略了什么。除了JUL之外,日志门面和日志实现的使用是如此广泛,以至于我不得不得出结论,是我自己不理解。恐怕这不是第一次了。: -)
那么我应该如何处理我的API呢?我希望它能成功。我当然可以“顺其自然”。并实现SLF4J(这似乎是目前最流行的),但为了我自己的缘故,我仍然需要确切地了解今天的JUL有什么问题,这保证了所有的模糊?我会因为选择JUL作为我的图书馆而破坏自己吗?
(nolan600于2012年7月7日新增)
下面有来自Ceki的关于SLF4J参数化比JUL快10倍或更多的参考。所以我开始做一些简单的测试。乍一看,这种说法当然是正确的。以下是初步结果(请继续阅读!):
上面的数字是msec,所以越少越好。所以10倍的性能差异实际上已经非常接近了。我的第一反应是:太多了!
这是测试的核心。可以看到,在循环中构造了一个整数和一个字符串,然后在log语句中使用:
for (int i = 0; i < noOfExecutions; i++) {
for (char x=32; x<88; x++) {
String someString = Character.toString(x);
// here we log
}
}
(我希望日志语句同时具有基本数据类型(在本例中为int)和更复杂的数据类型(在本例中为String)。我不确定这是否重要,但你已经知道了。)
SLF4J的日志语句:
logger.info("Logging {} and {} ", i, someString);
JUL的日志语句:
logger.log(Level.INFO, "Logging {0} and {1}", new Object[]{i, someString});
在实际测量完成之前,JVM被“预热”一次,执行相同的测试。Windows 7操作系统使用Java 1.7.03。使用了SLF4J (v1.6.6)和Logback (v1.0.6)的最新版本。标准输出和标准错误被重定向到空设备。
然而,现在要小心,原来JUL大部分时间都花在getSourceClassName()
中,因为JUL默认情况下在输出中打印源类名,而Logback则不会。所以我们在比较苹果和橘子。我必须再次进行测试,并以类似的方式配置日志实现,以便它们实际上输出相同的内容。然而,我确实怀疑SLF4J+Logback仍然会名列榜首,但远低于上面给出的初始数字。请继续关注。
顺便说一句:这个测试是我第一次真正使用SLF4J或Logback。愉快的经历。当你刚开始的时候,JUL当然不那么受欢迎。
(由nolan600于2012年7月8日新增)
事实证明,在JUL中如何配置模式对性能并不重要,即它是否包含源名称。我尝试了一个非常简单的模式:
java.util.logging.SimpleFormatter.format="%4$s: %5$s [%1$tc]%n"
这并没有改变上面的时间。我的剖析器显示,记录器仍然花费大量时间调用getSourceClassName()
,即使这不是我的模式的一部分。模式不重要。
因此,我在性能问题上得出的结论是,至少对于测试过的基于模板的日志语句来说,JUL(慢)和SLF4J+Logback(快)之间的实际性能差异大约是10倍。就像切奇说的。
我还可以看到另一件事,即SLF4J的getLogger()
调用比JUL的同上调用要昂贵得多。(95毫秒vs 0.3毫秒,如果我的分析器是准确的)。这是有道理的。SLF4J必须在底层日志实现的绑定上花费一些时间。这吓不倒我。在应用程序的生命周期中,这些调用应该很少。快速性应该体现在实际的日志调用中。
(部分由peter于2012年7月8日添加)
谢谢你的回答。与我最初的想法相反,我最终决定为我的API使用SLF4J。这是基于一些事情和你的输入:
JUL配置在应用服务器中运行时缺乏灵活性的问题。
所以发生的奇怪的事情是,在使用SLF4J工作了一段时间后,我实际上对JUL非常不满。我仍然很遗憾,JUL必须以这种方式出现。JUL远非完美,但也能做到这一点。只是还不够好。以Properties
为例也可以这样说,但我们不考虑对其进行抽象,以便人们可以插入自己的配置库等。我认为原因是Properties
刚好在酒吧之上,而相反的是今天的JUL…在过去,它是0,因为它不存在。
(部分由peter于02-OCT-2022添加)
Java 9引入了系统。日志记录器,它的目的是作为日志实现的facade。因此,据我所知,它与SLF4J竞争,但它的优势在于它包含在JDK中。因此,也许库开发人员应该使用System。Logger而不是SLF4J吗?
我发现Renato Athaydes的这篇博文很好地解释了它。(顺便说一句:Renato提到的Log4j-v2桥的bug似乎是Log4j v2的v2.13.2的固定)