我有生以来第一次发现自己正在写一个将开源的Java API。希望将其包含在许多其他项目中。
对于日志记录,我(实际上是与我一起工作的人)一直使用JUL(java.util.logging),并且从未遇到过任何问题。但是,现在我需要更详细地了解我应该为API开发做什么。我对此进行了一些研究,并根据所获得的信息感到更加困惑。因此,这个职位。
由于我来自七月,所以我对此有偏见。我对其余的知识并不多。
通过研究,我得出了人们不喜欢JUL的这些原因:
“我很早在Sun发布JUL之前就开始使用Java进行开发,对于我而言,继续使用logging-framework-X而不是学习新知识变得更加容易”。嗯 我不是在开玩笑,这实际上是人们所说的。有了这个论点,我们所有人都可以做COBOL。(但是我当然可以认为自己是个懒惰的家伙)
“我不喜欢JUL中的日志记录级别的名称”。好的,认真的说,这还不足以引入新的依赖关系。
“我不喜欢JUL输出的标准格式”。嗯 这只是配置。您甚至不必执行任何代码明智的事情。(是的,过去,您可能必须创建自己的Formatter类才能正确使用它)。
“我使用了其他也使用logging-framework-X的库,因此我认为仅使用该库就更容易了”。这是一个循环的论证,不是吗?为什么“每个人”都使用logging-framework-X而不使用JUL?
“其他人都在使用logging-framework-X”。对我来说,这只是上述情况的特例。多数并不总是正确的。
因此,真正的大问题是为什么不选择JUL?。我错过了什么?记录正面(SLF4J,JCL)的理由是,历史上已经存在多种记录实现,其原因确实可以追溯到我看到的JUL之前的时代。如果JUL是完美的,那么将不会存在伐木外观,还是什么?使事情更加混乱的是,JUL在某种程度上是一种外观,它允许交换Handlers,Formatters甚至LogManager。
而不是采用多种方式做同一件事(记录),我们是否应该首先质疑为什么必须这样做?(并查看这些原因是否仍然存在)
好的,到目前为止,我的研究已经发现了一些可能是JUL 真正问题的东西:
表现。有人说SLF4J的性能优于其他产品。在我看来,这似乎是过早优化的一种情况。如果您需要每秒记录数百兆字节,那么我不确定您是否走在正确的道路上。JUL也已经发展,您在Java 1.4上进行的测试可能不再正确。您可以在此处阅读有关此内容的信息,此修复程序已将其纳入Java7。许多人还谈到了日志记录方法中字符串连接的开销。但是,基于模板的日志记录避免了这种成本,并且它在JUL中也存在。我个人从来没有真正写过基于模板的日志记录。太懒了。例如,如果我使用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的性能与竞争对手相比不佳的信息。
从classpath配置。开箱即用的JUL无法从类路径加载配置文件。这样做需要几行代码。我可以理解为什么这可能会令人讨厌,但是解决方案又短又简单。
输出处理程序的可用性。JUL带有5个开箱即用的输出处理程序:控制台,文件流,套接字和内存。这些可以扩展,也可以编写新的。例如,这可能正在写入UNIX / Linux Syslog和Windows事件日志。我个人从来没有这个要求,也没有看到它使用过,但是我可以肯定地解释为什么它可能是一个有用的功能。例如,Logback附带了Syslog的附加程序。我仍然认为
- 开箱即用的JUL可以满足99.5%的输出目的地需求。
- 定制处理程序可以在JUL之上而不是其他之上满足特殊需求。对于我而言,没有什么可以比为其他日志记录框架花费更多的时间来为JUL编写Syslog输出处理程序的。
我真的很担心我忽略了某些事情。除了JUL之外,对日志外观和日志实现的使用是如此广泛,以至于我不得不得出结论,就是我自己不了解。恐怕这不是第一次。:-)
那么我应该如何处理我的API?我希望它成功。我当然可以“顺其自然”并实现SLF4J(这是当今最流行的),但是出于我自己的考虑,我仍然需要确切地了解今天的JUL到底有什么毛病?我会为图书馆选择JUL来破坏自己吗?
测试表现
(部分由nolan600在2012年7月7日添加)
以下来自Ceki的参考资料表明SLF4J的参数化速度是JUL的10倍甚至更快。所以我已经开始做一些简单的测试。乍一看,这一主张当然是正确的。这是初步结果(但请继续阅读!):
- 执行时间SLF4J,后端登录:1515
- 执行时间SLF4J,后端JUL:12938
- 执行时间七月:16911
上面的数字是毫秒,所以越少越好。所以实际上十倍的性能差异实际上是非常接近的。我最初的反应:很多!
这是测试的核心。可以看出,整数和字符串在循环中构造,然后在log语句中使用:
for (int i = 0; i < noOfExecutions; i++) {
for (char x=32; x<88; x++) {
String someString = Character.toString(x);
// here we log
}
}
(我希望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)。Stdout和stderr被重定向到空设备。
但是,现在要小心,事实证明JUL花费了大部分时间,getSourceClassName()
因为默认情况下,JUL在输出中显示源类名称,而Logback则不输出。因此,我们正在比较苹果和桔子。我必须再次进行测试,并以类似的方式配置日志记录实现,以便它们实际上输出相同的内容。但是,我确实怀疑SLF4J + Logback仍将排在首位,但与上面给出的初始数字相去甚远。敬请关注。
顺便说一句:该测试是我第一次真正使用SLF4J或Logback。令人愉快的经历。刚开始时,JUL肯定会受欢迎。
测试性能(第2部分)
(由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必须在底层日志记录实现的绑定上花费一些时间。这不吓我。这些调用在应用程序的生命周期中应该很少见。牢度应在实际的日志调用中。
定论
(由nolan600在2012年7月8日添加的部分)
感谢您的所有答复。与我最初的想法相反,我最终决定对我的API使用SLF4J。这基于许多因素和您的输入:
它提供了在部署时选择日志实现的灵活性。
在应用服务器内部运行时,JUL配置缺乏灵活性的问题。
SLF4J肯定比上面详述的要快得多,特别是如果将其与Logback结合使用。即使这只是一个粗略的测试,我也有理由相信,在SLF4J + Logback上进行的优化比在JUL上进行了更多的努力。
文档。SLF4J的文档更加全面和精确。
模式灵活性。在进行测试时,我开始让JUL模仿Logback的默认模式。此模式包括线程的名称。事实证明,JUL无法开箱即用。好的,直到现在我都没有错过它,但是我不认为日志框架应该缺少它。期!
如今,大多数(或许多)Java项目都使用Maven,因此添加依赖项并不是一件大事,尤其是如果该依赖项相当稳定,即不会不断更改其API。对于SLF4J似乎确实如此。SLF4J罐子和朋友的尺寸也很小。
所以发生的奇怪事情是,我在与SLF4J一起工作之后实际上对JUL感到不高兴。我仍然感到遗憾的是,JUL必须采取这种方式。JUL远非完美,但可以胜任。只是不够好。Properties
作为示例,可以说相同的内容,但是我们不考虑抽象化,因此人们可以插入自己的配置库以及您所拥有的。我认为原因是Properties
恰好在标准之上,而对于今天的7月则恰恰相反……过去,由于它不存在,所以归零。
java.lang.System.Logger
,它是一个接口,只要该框架能够实现并提供该接口的实现,它就可以重定向到所需的任何实际日志记录框架。结合模块化,您甚至可以部署带有不包含的捆绑JRE的应用程序java.util.logging
(如果您喜欢其他框架)。