SLF4jBridgeHandler 未拾取第 3 方日志记录,该日志记录器附加到根记录器,但随后被删除

问题描述 投票:0回答:1

我们的应用程序有使用 JUL 进行日志记录的第 3 方代码。我们的应用程序在类路径上有

jul-to-slf4j.jar
。我看到
SLF4JBridgeHandler.install()
方法被调用(多次),无论出于何种原因,spring 都会根据某些 ApplicationEvents 清理日志配置几次。我在
ApplicationStartedEvent
上创建了一个事件侦听器,以再次确保处理程序尽可能晚地安装。我通过检查
SLF4JBridgeHandler.isInstalled()
方法进行验证。然而,第 3 方日志不断流向标准输出。如果我在 IntelliJ 中对第 3 方日志记录类进行断点并调用
SLF4JBridgeHandler.isInstalled()
,它将返回 false。如果我执行下面的代码,日志就会被 slf4j 拾取,每个人都很高兴。

SLF4JBridgeHandler.install();
LogManager.getLogManager().getLogger("com.3rdparty").setUseParentHandlers(false);

为什么SLF4J总是被卸载?我如何保持它的安装状态?是否在我们安装 slf4j 之前创建了第 3 方记录器,然后没有通过 install() 调用进行更新?

spring spring-boot log4j java-11 java.util.logging
1个回答
1
投票

为什么SLF4J总是被卸载?

最常见的原因是有代码调用

LogManager.reset()
。您可以使用安全管理器查找源代码。

如果您使用logging.properties文件,那么只需将桥添加为处理程序即可。这样重置实际上就是安装桥处理程序。

handlers= java.util.logging.ConsoleHandler, org.slf4j.bridge.SLF4JBridgeHandler

唯一的缺点是桥处理程序必须对系统类加载器可见。您可以尝试的一个非常 hacky 的技巧是子类化 SLF4JBridgeHandler 并覆盖 equals 以返回 false。 LogManager.reset 将无法删除此处理程序。

Logger.getLogger("").addHandler(new SLF4JBridgeHandler() {
    public boolean equals(Object o) { return false;}
});

第 3 方记录器是否在我们安装 slf4j 之前创建,然后没有通过 install() 调用进行更新?

您必须做一些工作才能找到答案。 使用 JConsole Mbeans 或系统输出调试 在调试应用程序时遍历记录器树。安全管理器跟踪是找到负责删除处理程序的确切堆栈跟踪的最佳选择。

可以有两个根记录器吗?

在标准 LogManager 下这是不可能发生的。 Tomcat 有一个对上下文类加载器敏感的实现。 LogManager 可能会根据上下文类加载器返回相同记录器名称的不同实例。无需通过安全管理器即可删除处理程序的唯一其他方法是根记录器被垃圾收集。您可以尝试对根记录器进行强引用,但这实际上是不需要的。您应该在已知要添加处理程序时打印根记录器的身份哈希代码,并在删除处理程序后打印根记录器的身份哈希代码。如果值不同,记录器将被垃圾收集。

© www.soinside.com 2019 - 2024. All rights reserved.