我有生以来第一次发现自己处于编写开源 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)存在的理由是历史上已经存在多个日志记录实现,而在我看来,其原因实际上可以追溯到 7 月之前的时代。如果 JUL 是完美的,那么日志立面将不存在,还是什么?更令人困惑的是,JUL 在某种程度上本身就是一个外观,它允许交换处理程序、格式化程序甚至 LogManager。
与其采用多种方式来做同样的事情(记录),我们难道不应该首先质疑为什么它们是必要的吗?(看看这些原因是否仍然存在)
好的,到目前为止,我的研究导致了一些我可以看到可能是JUL真正问题的事情:
性能。有人说 SLF4J 的性能优于其他性能。在我看来,这似乎是过早优化的情况。如果您需要每秒记录数百兆字节,那么我不确定您是否走在正确的道路上。JUL 也在不断发展,您在 Java 1.4 上所做的测试可能不再适用。您可以在此处阅读有关它的信息,并且此修复程序已将其纳入 Java 7。许多人还谈到了日志记录方法中字符串连接的开销。然而,基于模板的日志记录避免了这种成本,它也存在于 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 系统日志和 Windows 事件日志。我个人从未有过这个要求,也没有见过它使用过,但我当然可以理解为什么它可能是一个有用的功能。例如,Logback 带有一个用于 Syslog 的附加程序。我仍然认为
- 99.5% 的输出目的地需求由 JUL 开箱即用的内容覆盖。
- 特殊需求可以通过 JUL 之上的自定义处理程序来满足,而不是在其他东西之上。对我来说,没有任何迹象表明为 JUL 编写 Syslog 输出处理程序比为另一个日志框架编写的时间更长。
我真的很担心我忽略了一些东西。除了 JUL 之外,日志门面和日志实现的使用如此广泛,以至于我不得不得出结论,就是我不明白。恐怕这不是第一次了。:-)
那么我应该如何处理我的 API 呢?我希望它成功。我当然可以“顺其自然”并实现 SLF4J(这似乎是当今最流行的),但为了我自己,我仍然需要确切地了解今天的 JUL 到底有什么问题,这保证了所有的模糊性?为我的图书馆选择 JUL 会破坏自己吗?
测试性能
(nolan600 于 2012 年 7 月 7 日添加的部分)
Ceki 下面有一个关于 SLF4J 的参数化比 JUL 快 10 倍或更多的参考。所以我开始做一些简单的测试。乍一看,这种说法肯定是正确的。以下是初步结果(但请继续阅读!):
- 执行时间 SLF4J,后端 Logback:1515
- 执行时间 SLF4J,后端 JUL:12938
- 执行时间7月:16911
上面的数字是毫秒,所以越少越好。所以 10 倍的性能差异首先实际上非常接近。我的第一反应是:太多了!
这里是测试的核心。可以看出一个整数和一个字符串在一个循环中构造,然后在 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)。标准输出和标准错误被重定向到空设备。
但是,现在小心,事实证明 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 倍。就像Ceki说的那样。
我还可以看到另一件事,即 SLF4J 的getLogger()
通话比 JUL 的同上要贵得多。(如果我的分析器准确,则为 95 毫秒对 0.3 毫秒)。这是有道理的。SLF4J 必须在底层日志实现的绑定上做一些时间。这并不吓到我。这些调用在应用程序的生命周期中应该很少见。牢度应该在实际的日志调用中。
定论
(nolan600 于 2012 年 7 月 8 日添加的部分)
感谢您的所有回答。与我最初的想法相反,我最终决定将 SLF4J 用于我的 API。这是基于许多事情和您的输入:
它提供了在部署时选择日志实现的灵活性。
在应用程序服务器中运行时,JUL 的配置缺乏灵活性的问题。
SLF4J 肯定比上面详述的要快得多,特别是如果你将它与 Logback 结合使用。即使这只是一个粗略的测试,我有理由相信在 SLF4J+Logback 上的优化要比在 JUL 上付出更多的努力。
文档。SLF4J 的文档更加全面和精确。
模式灵活性。当我做测试时,我开始让 JUL 模仿 Logback 的默认模式。此模式包括线程的名称。事实证明,JUL 不能开箱即用。好的,直到现在我还没有错过它,但我认为它不应该是日志框架中缺少的东西。时期!
今天大多数(或许多)Java 项目都使用 Maven,因此添加依赖项并不是什么大事,特别是如果该依赖项相当稳定,即不会不断更改其 API。这对于 SLF4J 来说似乎是正确的。SLF4J jar 和朋友的体积也很小。
所以发生的奇怪的事情是,在使用了 SLF4J 之后,我实际上对 JUL 非常不满。我仍然很遗憾 JUL 必须这样。JUL 远非完美,但可以胜任。只是不够好。可以Properties
作为一个例子说同样的事情,但我们不考虑抽象它以便人们可以插入他们自己的配置库和你有什么。我认为原因是它Properties
刚好在酒吧上方,而今天的 JUL 正好相反……过去它是零,因为它不存在。