繁体   English   中英

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

[英]Is it worth to use slf4j with log4j2

我无法决定是否将 slf4j 与 log4j2 一起使用。 根据在线帖子,它看起来不会对性能造成任何影响,但它真的是必需的。

这些点也有利于 log4j2:

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

继续:编程到 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 可以提供更多功能并且具有一些非功能性优势:

  • 消息接口
  • 用于延迟日志记录的 Lambda
  • 记录任何对象而不仅仅是字符串
  • 无垃圾:尽可能避免创建可变参数或创建字符串
  • 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 所需的依赖项类似

有很多考虑因素使日志记录“比乍一看更复杂”,(因此是数十年的苦战!)。

关注点分离

在一天结束时,代码“发送日志数据”并且该数据“最终到达某处”。 但它的最终位置取决于收集它的目的。 使情况大大复杂化的是,现代软件由各种组件组成,而且它们都可能需要记录日志。

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

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

我们想要介于两者之间的东西:代码可以说“记录这个”并且管理员可以控制它结束的地方。

过于简短的历史

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

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

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

Java Community Process 神秘地蔑视 Log4J 的工作,决定在java.util.logging实现几乎相同的概念架构,但在细节上的灵活性可能会降低。 然而,由于jul本质上是 SLF4J 语义丰富性的一个子集,所以很容易让 SLF4J 成为jul的门面。

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

不同情况下的日志

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

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

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

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

底线

如果您想要 Log4J2 提供的功能,请选择它们。 如果你不需要它们,SLF4J 是一个成熟、稳定的接口,有很多支持。 SLF4J 仍然是开源基础设施的重要组成部分。 Ceki 为社区提供了很好的服务,作为回报,很多抱怨。

但最终还是由有能力的实现支持的丰富的 API 盛行。 今天的稳定就是明天的停滞。 细化的过程一直在进行。 不需要下车,只要它去你想去的地方。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM