是否值得将slf4j与log4j2一起使用

And*_*897 99 java logging log4j slf4j log4j2

我无法决定是否使用slf4j与log4j2.基于在线帖子,看起来不会有任何性能影响,但它确实是必需的.

这些点也有利于log4j2:

  • SLF4J强制您的应用程序记录字符串.如果要记录文本,Log4j 2 API支持记录任何CharSequence,但也支持按原样记录任何Object.
  • Log4j 2 API支持记录Message对象,Java 8 lambda表达式和无垃圾日志记录(它避免了创建vararg数组并避免在记录CharSequence对象时创建字符串).

Rem*_*pma 137

继续:编程到log4j2 API而不是slf4j

它是安全的:Log4j2 API提供与slf4j完全相同的保证 - 以及更多.

既然Log4j2本身被分成了API和一个实现模块,那么使用SLF4J就没有任何价值了.

是的,保持选项开放是一种很好的工程实践.您可能希望稍后更改为另一个日志记录实现.

在过去10年左右的时间里,在应用程序中构建这样的灵活性意味着使用像SLF4J这样的包装器API.这种灵活性并非免费提供:此方法的缺点是您的应用程序无法使用底层日志记录库的更丰富的功能集.

Log4j2提供的解决方案不要求您的应用程序仅限于最低公分母.

逃生阀:log4j-to-slf4j

Log4j2包括log4j-to-slf4j桥接模块.任何针对Log4j2 API编码的应用程序都可以选择随时将支持实现切换到任何符合slf4j的实现.

log4j的到SLF4J

正如问题中所提到的,与使用像slf4j这样的包装器API相比,使用Log4j2 API可以直接提供更多功能,并且具有一些非功能性优势:

  • 消息API
  • 用于懒惰记录的Lambdas
  • 记录任何Object而不仅仅是字符串
  • 无垃圾:尽可能避免创建varargs或创建字符串
  • 完成后,CloseableThreadContext会自动从MDC中删除项目

(有关详细信息,请参阅SLF4J中未提供的10个Log4j2 API功能.)

应用程序可以安全地使用Log4j2 API的这些丰富功能,而无需锁定到本机Log4j2核心实现.

SLF4J仍然是您的安全阀,它并不意味着您的应用程序应该再对SLF4J API进行编码.


披露:我为Log4j2做出贡献.


更新:似乎有些混淆,Log4j2 API的编程以某种方式引入了"门面的外观".Log4j2 API和SLF4J在这方面没有区别.

在使用本机实现时,这两个API都需要2个依赖项,而对于非本机实现,这两个依赖项需要4个依赖项.SLF4J和Log4j2 API在这方面是相同的.例如:

SLF4J和Log4j 2 API的必需依赖项类似

  • 那么,我应该使用接口作为实现的接口?是的,不,谢谢...... Slf4j通过为日志记录提供了一个良好的接口来击败log4j ... log4j2应该只实现slf4j api - 如果有一个缺失的功能,贡献它回来,如果slf4j不会采取新的功能,然后可能有一个log4j2 api接口的情况.... (16认同)
  • 问题是你经常依赖于自己使用slf4j的库,所以更容易坚持使用它. (15认同)
  • 我明白.让我重新解释一下我的问题.是否有除log4j2以外的log4j2 API的独立实现? (5认同)
  • Log4j2 API和impl**不是**"紧密耦合".所有这些SLF4J实现都可用:针对Log4j2 API编写的应用程序可以选择`log4j-to-slf4j`依赖项而不是`log4j-core`,并选择您提到的任何这些SLF4J实现.Log4j2 API的_native_实现的数量是无关紧要的. (4认同)
  • @RemkoPopma - 你仍然主张不使用log4j接口.是的,我明白了 - 我可以链接log4j2的界面 - > slf4j的界面 - >无论实现什么,但我只是不抽象抽象 - 谢谢,不,谢谢. (3认同)
  • @RockMeetHardplace 我假设您正在使用 Log4j2 实现。在这种情况下,它只是 Log4j2 API-> Log4j2 impl。我不明白为什么有人想做 slf4j-> Log4j2 API-> Log4j2 impl。_That_ 将抽象化抽象,而_that_ 正是我所提倡反对的。 (3认同)
  • @TJR SLF4J可能不会以其当前形式支持大部分Log4j2的功能.这样做需要将SLF4J重新设计为与Log4j2 API基本匹配的东西,并且不会向后兼容非Log4j2实现.log4j2的全部内容是它的API.几乎所有log4j2的"后端"功能都可以在Logback中使用.Log4j2存在的全部原因是为了解决SLF4J等"经典"API的局限性.使用新API,我们可以转移到异步记录器而不是异步appender,使用lambdas代替记录器保护,并使用丰富的消息 (3认同)
  • 只要API和实现紧密耦合,API和实现分离就没有意义.我认为log4j2 API和impl是紧密耦合的.是?在SLF4J API的情况下,有许多实现,包括java.logging,log4j 1,log4j 2,logback和Groovy,Scala,Clojure ...... (2认同)
  • Log4j2 API 不依赖于 Log4j2 实现。依赖关系严格来说是一种方式,它应该是这样。正如我所说,不存在对本机实现的锁定。 (2认同)
  • 我同意.只是调用Log4j2 API和实现"紧密耦合"也不会成为现实.紧耦合是一个[明确定义的技术术语] [1],这里不适用.如果我们遵循您的逻辑,那么每个只有一个实现类的Java接口将与该类紧密耦合.事实显然并非如此.[1]:https://en.m.wikipedia.org/wiki/Coupling_ (computer_programming) (2认同)
  • 我应该注意你关于“log4j-to-slf4j”适配器模块的评论,这使得我上面的评论毫无意义。 (2认同)
  • @RockMeetHardplace Log4j2 _does_实现了slf4j api,只包含`log4j-slf4j-impl`依赖.它还提供自己的API,具有更多功能和一些非功能性优势.SLF4J可以自由采用这些(毕竟它是开源的)但它自然需要担心向后兼容性.我的观点是,直接编程到Log4j2 API是安全的,因为它提供了与SLF4J提供的交换实现相同的保证. (2认同)
  • Re:在这种情况下,它只是Log4j2 API-> Log4j2 impl ..... @ RemkoPopma - 伙计,你没有得到它......我知道它是如何工作的,你所倡导的是我进去并改变所有我的代码使用log4j2而不是slf4j.没有. (2认同)
  • 我同意,对于现有应用程序,使用`log4j-slf4j-impl`模块而不是修改所有代码应该没问题。我一定不清楚,对不起。不需要修改代码,这就是全部内容,而我想说的是,Log4j2 API在此方面与slf4j一样出色。对于新项目,我鼓励人们直接使用Log4j2。 (2认同)
  • 实际上,根据[log4j-to-slf4j文档],与`slf4j-api`相比,`log4j2 + log4j-to-slf4j`有一个性能缺陷(https://logging.apache.org/log4j/2.x /log4j-to-slf4j/index.html):_使用此适配器可能会导致性能下降,因为Log4j 2消息必须先格式化,然后才能传递给SLF4J。使用Log4j 2作为实现,通常只有在由Filter或Appender访问它们时才会格式化它们。 (2认同)

Dav*_*ock 19

有相当多的考虑因素使得日志记录“比乍一看更复杂”(因此出现了数十年的激烈内讧!)。

关注点分离

最终,代码“发送日志数据”并且该数据“最终到达某个地方”。但它的最终去向取决于收集它的目的。使情况变得更加复杂的是,现代软件是由各种组件组成的,并且它们都可能需要记录。

让我们考虑最坏的情况:所有组件都使用System.out#println(String). 至少所有语句都是按执行顺序排列的,但是辨别哪个组件生成每段输出可能并不容易。有些组件对于使用它们的上下文来说可能过于冗长。

让我们考虑下一个最坏的情况:所有组件都做出自己的安排来控制其日志记录行为和目的地。管理员可能必须为单个软件配置数十个日志系统。现在日志语句不在一起并且顺序混乱。希望他们都有一致的时间戳策略!

我们想要介于两者之间的东西:代码可以说“记录这个”,而管理员可以控制它最终的位置。

历史过于简短

输入 Log4J v1,它通过“级别”、“附加程序”、“过滤器”、“布局”和“上下文”等概念解决了问题……由分层“记录器名称空间”支持的概念架构(包括自然利用的方法) Java 包命名空间),加上易于管理的配置机制。

这一切都很好......只要软件中的所有组件都依赖于相同的版本!曾经有一段时间,这些事情一直在变化。SLF4J 的主要贡献是从组件开发人员的角度将这些概念“强化”为稳定的 API,而不影响管理员完成其部分工作的选择。图书馆可以依赖 SLF4J“外观”,期望他们只需在堆栈中进行几次调用即可与“实现”进行对话。管理员可以选择适合他们的方式将日志组合成他们关心的连贯记录。

(当你的软件在容器中运行时,情况会更加复杂,并且容器有自己的日志记录需求,而你甚至不是在容器中运行的同一个应用程序......Tomcat的JULI日志记录 - 用于其自己的内部日志记录 - “避开”在类加载器子上下文中运行的应用程序。)

Java Community Process 神秘地蔑视 Log4J 的工作,决定在 中实现几乎相同的概念架构java.util.logging,但细节上的灵活性可能较低。然而,由于j.u.l本质上是 SLF4J 语义丰富性的子集,因此很容易使 SLF4J 成为j.u.l.

Apache 的 Commons Util Logging 可能不是很有必要。Ceki 自己的 Logback 引入了当时 Log4J v1 所没有的管理功能 - 不仅是 SLF4J 的实现并解决了所有那些非常现实的类加载器难题,而且还为管理员提供了一些有吸引力的功能的有效实现。

针对不同情况进行日志记录

但日志记录是在许多不同的上下文中完成的。将这些消息输出到超慢的 I/O 中,而不会过度阻塞线程,除非需要,否则不会付出计算日志消息的代价,并在多线程上下文中生成一致的日志……这些事情都很重要。(这就是为什么java.util.logging不经常使用的原因!)。

有时,所需的优化会影响概念架构,而概念架构又必然会影响开发人员端 API。例如,如果日志消息由于过滤而最终成为无操作,则闭包提供的机会肯定会加快速度。需要考虑使用 SLF4J.next 或其他一些 API 来利用该功能,并且 Log4J2 不需要被排除在该决定之外。由于 API 部分是 SLF4J 提供的概念超集,因此很容易将其作为 SLF4J 及其下的实现的外观……或更直接地连接到管理员所喜欢的内容。

对于应用程序开发人员来说,这并不重要,只要管理员最终选择一种日志记录工具,并且所有组件都可以从该工具中注销即可。如果该设施可以通过 SLF4J和Log4J2-the-API接受消息,那就太好了。Log4J2 的实现就是这样做的。您可以鱼与熊掌兼得:您的应用程序可以享受 Log4J2-the-API 提供的机会,同时仍然使用 SLF4J 充分满足的库。如果管理员鄙视 Log4J2-the-implementation(尽管从任何角度都很难看出他们为什么会这样做),那么他们可以使用任何已经支持 SLF4J 的东西,而无需等待该日志记录实现支持 Log4J2-the-API。你可以鱼与熊掌兼得。

对于库开发人员来说,这更是一个问题。安全路径是 SLF4J,因为它被广泛采用。如果日志记录对于您的库的成功至关重要......特别是如果它是多线程的,则日志语句的生成成本可能很高,并且如果它们最终不会被消耗,如果存在以下情况,最好省略处理:要处理大量日志语句,性能至关重要,并且您的用户可能会欣赏 Log4J2 实现的好处,然后使用 Log4J2。但继续使用 SLF4J 也不会窃取用户的机会。如果管理员愿意,他们仍然可以使用 Log4J-the-implementation。

底线

如果您想要 Log4J2 提供的功能,那就去吧。如果您不需要它们,SLF4J 是一个成熟、稳定的接口,有很多支持。SLF4J 仍然是开源基础设施的重要组成部分。Ceki 为社区做出了巨大的贡献,但也带来了很多抱怨。

但由有能力的实现支持的丰富 API 最终占上风。今天的稳定就是明天的停滞。细化的过程仍在继续。无需下车,只要它开往您想去的地方即可。

  • 我对此有几点评论。commons-logging 是“必要的”,因为它创建于 2001 年,大约比 SLF4J 早 5 年。至于 SLF4J 支持,从 2020 年初到今天,Ceki 已经关闭了 2 个 PR,没有合并任何一个,执行了 1 次提交来记录 ICLA,在 2020 年上半年回复了 3 封电子邮件(此后没有回复),并将一个 Jira 问题发布到要求 ICLA。他今年对 Logback 做出了一项小小的承诺,并偶尔在 Twitter 上发帖,所以我认为他还不错,但我不确定发生了什么。但我同意他值得大量赞誉。 (6认同)