这是我人生中第一次发现自己处在这样一个位置:我正在编写一个开源的API,希望能被包括在许多其他项目中。
对于日志记录,我(实际上还有我的同事)一直使用JUL(java.util.logging
),从来没有遇到过任何问题。但是现在我需要更详细地了解我应该为我的API开发做些什么。我对此做了一些研究,我得到的信息让我更加困惑。因此写了这篇文章。
因为我是七月来的,所以我对这个有偏见。我对其他的了解不是很多。
根据我所做的研究,我得出了人们不喜欢JUL的以下原因:
1.* “早在Sun发布JUL之前,我就开始用Java开发了,对我来说,继续使用logging-framework-X比学习新的东西更容易”。嗯。我不是开玩笑,这确实是人们所说的。有了这个论点,我们都可以做COBOL。(然而,我肯定自己是个懒惰的家伙)
1. “我不喜欢JUL中日志级别的名称”.好吧,说真的,这还不足以成为引入新依赖项的理由。
1. “I don 't like the standard format of output from JUL”.嗯。这只是配置。你甚至不需要做任何代码方面的事情。(的确,在过去,你可能需要创建自己的Formatter类来使它正确)。
1. “我使用的其他库也使用了logging-framework-X,所以我认为只使用那个库更容易”.这是一个循环论证,不是吗?为什么'everybody'使用logging-framework-X而不是JUL?
1. “其他所有人都在使用logging-framework-X”*。对我来说,这只是上述情况的一个特例。多数人并不总是正确的。
所以真实的的大问题是为什么不是7月?我错过了什么?伐木外墙存在的理由(SLF 4J,JCL)的一个重要问题是历史上已经存在多个日志实现,其原因可以追溯到JUL之前的时代。如果JUL是完美的,那么日志外观就不会存在,让事情更混乱的是,JUL在某种程度上本身就是一个门面,允许处理程序、格式化程序甚至日志管理器进行交换。
与其采用多种方法来做同一件事(日志记录),我们难道不应该首先质疑为什么它们是必要的吗?(并看看这些原因是否仍然存在)
好吧,我的研究到目前为止已经导致了一些事情,我可以看到可能是真实的的问题与七月:
1.性能。有人说SLF 4J的性能上级其他软件。在我看来,这是一个过早优化的例子。如果你需要每秒记录数百兆字节,那么我不确定你走的路是否正确。JUL也在发展,你在Java 1.4上做的测试可能不再正确。你可以在here上读到它,这个修复已经进入了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的性能与竞争对手相比不好。
1.来自类路径的配置.开箱即用的JUL不能从类路径加载配置文件。这是一个few lines of code使它这样做。我可以理解为什么这可能是恼人的,但解决方案是简短和简单的。
1.输出处理程序的可用性. JUL自带5个输出处理程序:控制台、文件流、套接字和内存。这些都可以扩展,也可以写入新的。例如,这可能是写入UNIX/Linux Syslog和Windows事件日志。我个人从未有过这个要求,也没有见过它的使用,但我可以肯定地说,为什么它可能是一个有用的功能。例如,Logback附带了一个Syslog的附加器。但我仍然认为
- 99.5%的输出目的地需求由7月开箱即用的产品覆盖。
1.特殊的需求可以通过在JUL之上而不是在其他东西之上定制处理程序来满足。我并不认为为JUL编写一个Syslog输出处理程序比为其他日志框架编写一个Syslog输出处理程序需要更多的时间。
我真的很担心我忽略了一些东西。除了JUL之外,日志外观和日志实现的使用是如此普遍,以至于我不得不得出结论,那就是我不明白。恐怕这不是第一次了。:-)
那么我应该如何处理我的API呢?我希望它能够成功。我当然可以“顺其自然”并实现SLF 4J(这似乎是目前最流行的),但为了我自己的利益,我仍然需要确切地了解今天的JUL到底有什么问题,值得所有的模糊?我会因为选择JUL作为我的库而破坏自己吗?
测试性能
- (nolan 600于2012年7月7日添加的章节)*
下面有一篇来自Ceki的参考文章,说SLF 4J的参数化比JUL快10倍甚至更多。所以我开始做一些简单的测试。乍一看,这个说法当然是正确的。下面是初步结果(但请继续阅读!):
- 执行时间SLF 4J,后端Logback:1515
- 执行时间SLF 4J,后端7月:12938
- 执行时间7月:16911
上面的数字是毫秒,所以越少越好。所以10倍的性能差异最初实际上相当接近。我的初步React是:这是很多!
下面是测试的核心,可以看到一个整数和一个字符串在一个循环中被构造,然后在日志语句中使用:
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)。虽然不确定这是否重要,但您已经做到了。)
SLF 4J的日志语句:
logger.info("Logging {} and {} ", i, someString);
7月的日志语句:
logger.log(Level.INFO, "Logging {0} and {1}", new Object[]{i, someString});
在进行实际测量之前,JVM已通过执行一次相同测试进行“预热”。Windows 7上使用Java 1.7.03。使用最新版本的SLF 4J(v1.6.6)和Logback(v1.0.6)。Stdout和stderr被重定向到空设备。
但是,现在要小心了,JUL大部分时间都花在getSourceClassName()
上,因为JUL默认在输出中打印源类名,而Logback则不然。因此,我们是在比较苹果和桔子。我必须再次进行测试,并以类似的方式配置日志记录实现,以便它们实际上输出相同的内容。Logback仍然会排在第一位,但与上面给出的初始数字相差甚远。敬请关注。
顺便说一句:这次测试是我第一次真正使用SLF 4J或Logback。一次愉快的经历。当你刚开始的时候,JUL肯定不太受欢迎。
测试性能(第2部分)
- (nolan 600于2012年7月8日添加的章节)*
事实证明,在JUL中如何配置模式对性能并不重要,也就是说,它是否包含源代码名称。
java.util.logging.SimpleFormatter.format="%4$s: %5$s [%1$tc]%n"
而这并没有改变上面的计时。我的分析器显示记录器仍然花了很多时间调用getSourceClassName()
,即使这不是我的模式的一部分。
因此,我对性能问题的结论是,至少对于测试过的基于模板的日志语句来说,JUL(慢)和SLF 4J +Logback(快)之间的真实的性能差异似乎大约是10倍,就像Ceki所说的那样。
我还可以看到另一件事,即SLF 4J的getLogger()
调用比JUL的同上要昂贵得多。(95毫秒对0.3毫秒,如果我的分析器是准确的话)。这是有道理的。SLF 4J必须在底层日志实现的绑定上花费一些时间。这并不吓到我。这些调用在应用程序的生命周期中应该是比较少的。坚牢度应在实际对数呼叫中。
最终结论
- (peterh于2012年7月8日添加的章节)*
谢谢你们的回答。与我最初的想法相反,我最终决定使用SLF 4J作为我的API。这是基于许多因素和你们的意见:
1.它提供了在部署时选择日志实现的灵活性。
1.在应用服务器内部运行时,JUL的配置缺乏灵活性。
1.正如上面详细介绍的那样,SLF 4J确实要快得多,特别是如果您将其与Logback结合使用的话。即使这只是一个粗略的测试,我也有理由相信,与JUL相比,在SLF 4J +Logback上进行优化的工作要多得多。
1.文档。SLF 4J的文档要全面和精确得多。
1.模式灵活性。在我做测试的时候,我让JUL模拟Logback的默认模式。这个模式包括线程的名称。事实证明JUL不能在开箱即用的情况下做到这一点。好吧,我到现在还没有错过它,但我不认为它是日志框架中应该缺少的东西。句号!
1.现在大多数(或许多)Java项目都使用Maven,所以添加依赖项不是什么大问题,特别是如果依赖项相当稳定,即不会经常更改其API。SLF 4J似乎也是如此。此外,SLF 4J jar和朋友的大小都很小。
奇怪的是,在使用SLF 4J一段时间后,我对JUL感到很不高兴。我仍然很遗憾,JUL必须这样。JUL远非完美,但有点像是在做这项工作。只是不够好。以Properties
为例也是如此,但我们不考虑抽象它,以便人们可以插入他们自己的配置库和我认为原因是Properties
刚好在标准线之上,而今天的7月正好相反...过去它是零,因为它不存在。
最终-最终结论(可能)
- (peterh于2022年10月2日添加的章节)*
Java 9引入了System.Logger,它是作为日志实现的一个门面。因此,据我所知,它与SLF 4J竞争,但有一个优势,它包含在JDK中。因此,也许库开发人员应该使用System.Logger而不是SLF 4J?
我发现Renato Athaydes的this blog post解释得很好。Renato提到的Log4j-v2网桥中的错误似乎是Log4j v2的v2.13.2的fixed)
6条答案
按热度按时间iszxjhcz1#
选择SLF4J有一些客观原因。首先,SLF4J允许最终用户自由选择底层日志框架。此外,精明的用户倾向于选择logback which offers capabilities beyond log4j,j.u. l则远远落后。功能方面的j.u. l可能对一些用户来说已经足够了,但对其他许多用户来说还不够。简而言之,如果日志对您很重要,您可能希望使用带有logback的SLF4J作为底层实现。如果日志记录不重要,那么j.u. l就可以了。
然而,作为一个开源软件开发者,你需要考虑用户的偏好,而不仅仅是你自己的。因此,你应该采用SLF4J,不是因为你确信SLF4J比j.u. l更好,而是因为大多数Java开发者目前(2012年7月)更喜欢SLF4J作为他们的日志API。如果最终你决定不在乎流行的观点,考虑以下事实:
1.那些喜欢j.u. l的人是出于方便,因为j.u. l是与JDK捆绑在一起的。2据我所知,没有其他支持j.u. l的客观论据。
1.你自己对j.u. l的偏爱只是,一种偏爱。
因此,将"铁的事实"凌驾于公众舆论之上,虽然看起来很勇敢,但在这种情况下是一种逻辑谬误。
如果仍然不相信,JB Nizet会提出一个额外的有力论据:
除了最终用户可能已经为他自己的代码或者其他使用log4j或logback的库做了这个定制之外。j.u. l是可扩展的,但是必须扩展logback、j.u. l、log4j和天知道是哪一个其他的日志框架,因为他使用四个库,而这四个库使用四个不同的日志框架,这是很麻烦的。通过使用SLF4J,您允许他配置他想要的日志框架,而不是你选择的那个。记住一个典型的项目使用无数的库,而不仅仅是你的库。
如果出于某种原因,您讨厌SLF4J API,并且使用它会扼杀您工作中的乐趣,那么无论如何都要使用j.u.l.。毕竟,有一些方法可以将j.u.l.重定向到SLF4J。
顺便说一下,j.u. l参数化比SLF4J慢至少10倍,这最终产生了明显的差异。
j2qf4p5b2#
java.util.logging
是在Java 1.4中引入的。在此之前就有日志记录的用途。这就是为什么有许多其他日志记录API存在的原因。这些API在Java 1.4之前被大量使用,因此拥有巨大的市场份额,而不是在1.4发布时下降到零。1.你不能总是影响你的库使用什么样的日志框架,因此使用SLF 4J(它实际上只是其他库之上的一个非常薄的API层)有助于保持整个日志世界的某种一致性(这样你就可以决定底层日志框架,同时在同一个系统中仍然有库日志)。
1.库不能轻易地改变。如果一个库的早期版本使用logging-library-X,它不能轻易地切换到logging-library-Y(例如JUL),即使后者明显上级。该库的任何用户都需要学习新的日志框架,并(至少)重新配置他们的日志。这是一个大禁忌,特别是当它没有给大多数人带来明显的好处时。
说了这么多之后,我认为JUL至少是目前其他日志框架的一个有效替代品。
yfjy0ee73#
恕我直言,使用slf4j这样的日志外观的主要优点是,您可以让库的最终用户选择他想要的具体日志实现,而不是将您的选择强加给最终用户。
也许他已经在Log4j或LogBack(特殊的格式化程序、附加程序等)上投入了时间和金钱,并且更喜欢继续使用Log4j或LogBack,而不是配置jul。slf4j允许这样做。使用Log4j而不是jul是明智的选择吗?也许是,也许不是。但是您不在乎。让最终用户选择他喜欢的。
6mw9ycah4#
我开始使用JUL,就像我怀疑的你一样,因为它是最容易立即开始的一个。然而,多年来,我开始希望我花了更多的时间来选择。
我现在的主要问题是,我们有大量的“库”代码在许多应用程序中使用,他们都使用JUL。每当我在Web服务类型的应用程序中使用这些工具时,日志记录就会消失,或者出现不可预测或奇怪的情况。
我们的解决方案是在库代码中添加一个facade,这意味着库日志调用不会更改,而是被动态重定向到任何可用的日志机制。当包含在POJO工具中时,它们被重定向到JUL,但当部署为Web应用程序时,它们被重定向到LogBack。
当然,我们感到遗憾的是,库代码没有使用参数化日志记录,但现在可以根据需要进行改进。
我们使用slf4j来构建外观。
7fhtutme5#
我在logback-1.1.7上运行了jul对slf 4j-1.7.21,输出到SSD,Java 1.8,Win 64
对于1 M循环,7月运行48449 ms,回滚27185 ms。
尽管如此,多一点的速度和更好的API对我来说不值3个库和800 K。
以及
vaj7vani6#
不选择7月的原因:
1.用户不友好的API设计--笨拙的消息参数语法,奇怪的日志级别定义......不胜枚举。
1.它是一个日志记录实现,而不是一个外观。
正如引用的博客文章所指出的,Java 9 System.Logger的一个非目标是为日志记录定义一个通用接口。SLF 4J和Apache Commons Logging都尝试过这样的Facade目标,但IMHO都失败了,因为它们提供了太多的实现,而不是坚持一个定义良好的日志记录Facade的简单目标。一个真实的的日志facade永远不会在“性能”上与任何日志实现进行比较,因为facade本身并不期望进行日志记录。
免责声明:对于日志外观,我重新发明了wheel并使用了我自己的-https://github.com/elf4j/elf4j/