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

Is it worth to use slf4j with log4j2

我无法决定是否将 slf4j 与 log4j2 一起使用。根据在线帖子,它看起来不会对性能产生任何影响,但确实需要。

此外,这些要点也支持 log4j2:

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

很安全:Log4j2 API 提供与 slf4j 完全相同的保证 - 甚至更多。

现在Log4j2本身被分离成一个API和一个实现模块,使用SLF4J已经没有任何价值了。

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

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

Log4j2 提供的解决方案不要求您的应用程序受限于最小公分母。

逃生阀:log4j-to-slf4j

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

如问题中所述,与使用 slf4j 等包装器 API 相比,直接使用 Log4j2 API 可提供更多功能并具有一些非功能性优势:

  • 留言API
  • 用于惰性日志记录的 Lambdas
  • 记录任何对象而不仅仅是字符串
  • 无垃圾:尽可能避免创建可变参数或创建字符串
  • CloseableThreadContext 会在您使用完项目后自动从 MDC 中删除项目

(有关详细信息,请参阅 。)

应用程序可以安全地使用 Log4j2 API 的这些丰富特性,而不必局限于本机 Log4j2 核心实现。

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


披露:我为 Log4j2 做贡献。


更新:对于 Log4j2 API 编程以某种方式引入了“外观的外观”,这似乎有些混乱。 Log4j2API和SLF4J

在这方面没有区别

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

Required dependencies with log4j-api as API with SLF4J as API
Log4j 2 as implementation 2: log4j-api and log4j-core 4: slf4j, log4j-slf4j-impl, log4j-api, log4j-core
Logback as implementation 4: log4j-api, log4j-to-slf4j, slf4j, Logback 2: slf4j and Logback

有很多考虑因素使得日志记录“比乍看起来更复杂”,(因此数十年的激烈内斗!)。

关注点分离

一天结束时,代码 'sends log data' 和数据 'ends up somewhere'。但它的最终去向取决于收集它的目的。使情况更加复杂的是,现代软件由各种组件组成,它们都可能需要记录。

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

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

我们想要介于两者之间的东西:代码可以说 'log this' 并且管理员可以控制它结束的地方。

过于简短的历史

进入 Log4J v1,它使用 'levels'、'appenders'、'filters'、'layouts' 和 'contexts' 等概念解决了问题……一个概念架构由分层 'logger namespace' 支持(包括自然利用 Java 包命名空间的方法),以及易于管理的配置机制。

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

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

神秘地蔑视 Log4J 的工作,Java Community Process 决定在 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 and Log4J2-the-API 接受消息,那就太好了 。 Log4J2-the-implementation 就是这样做的。您可以吃蛋糕也可以吃:您的应用程序可以享受 Log4J2-the-API 提供的机会,同时仍然使用 SLF4J 充分满足的库。如果管理员蔑视 Log4J2-the-implementation(尽管从任何角度都很难看出他们为什么会这样做),那么他们可以使用任何已经支持 SLF4J 的东西,而无需等待该日志记录实现支持 Log4J2-the-API.你可以吃你的蛋糕。

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

底线

如果您想要 Log4J2 提供的功能,请使用它们。如果您不需要它们,SLF4J 是一个成熟、稳定的接口,并提供大量支持。 SLF4J 仍然是开源基础设施的重要组成部分。 Ceki 为社区做出了巨大的贡献,在 return.

中引起了很多关注

但丰富的 API 由有能力的实施支持最终占了上风。今天的稳定就是明天的停滞。细化的过程一直在进行。不用下车只要能开到你想去的地方