日志JUL 至 SLF4J 桥接

JUL-to-SLF4J 桥接增强了我们的日志记录功能,同时保持了与 JUL 的兼容性。通过集成 SLF4J,我们获得了强大的日志记录 API 和更高的灵活性。对于旨在改进日志记录实践并确保可维护性的开发人员来说,此桥接是一个有价值的补充。

JUL 是 Java 的内置日志框架。它使用起来很简单,但缺乏现代替代方案所提供的灵活性和高级功能。因此,许多应用程序选择了其他东西。

但是在这个模块化软件和第三方依赖的世界中,我们的应用程序中的某些东西可能会使用 JUL,尽管我们不愿意这样做。这使得正确配置日志变得更加复杂。

在本文中,我们将了解SLF4J的 JUL 到 SLF4J桥如何帮助解决这个问题。

依赖项
首先,我们在项目中包含 SLF4J 依赖项。对于 Maven 项目,我们需要将slf4j-api 和jul-to-slf4j 依赖项添加到pom.xml文件中:

<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>2.0.16</version>
</dependency>
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>jul-to-slf4j</artifactId>
    <version>2.0.16</version>
</dependency>

jul-to-slf4j工件由java.util.logging (JUL) 处理程序(即SLF4JBridgeHandler)组成,它将所有传入的 JUL 记录路由到 SLF4J API。

请注意,应用程序还必须指定要使用的 SLF4J API SLF4J 日志记录实现。因此,对于本文,我们假设我们的应用程序使用slf4j-simple:

<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-simple</artifactId>
    <version>2.0.16</version>
</dependency>

默认 JUL 日志
在配置桥接器之前,如果库通过 JUL 进行日志记录,则输出将使用默认的 JUL 格式。例如,请考虑以下代码:

import java.util.logging.Logger;
public class BusinessLogic {
    static void julLog(Logger julLogger) {
        julLogger.info("This is a JUL info log message!");
        julLogger.warning("This is a JUL warning log message!");
    }
}

在没有 JUL-to-SLF4J 桥接器的情况下运行此代码时,julLog()会产生类似以下内容的输出:

Jan 19, 2025 11:43:41 PM com.jdon.BusinessLogic julLog INFO: This is a JUL info log message!
Jan 19, 2025 11:43:41 PM com.jdon.BusinessLogic julLog WARNING: This is a JUL warning log message!

配置网桥
我们可以通过两种方式之一配置桥接器:通过编程或通过logging.properties文件。让我们分别看一下。

程序化配置
我们可以在主应用程序类中以编程方式设置 SLF4J 桥接器。此方法非常简单,可确保在应用程序的初始化阶段安装桥接器:

import org.slf4j.bridge.SLF4JBridgeHandler;
import java.util.logging.Logger;
public static void main(String[] args) {
    // Remove existing handlers
    SLF4JBridgeHandler.removeHandlersForRootLogger();
    // Install SLF4J bridge
    SLF4JBridgeHandler.install();
    // Test output after slf4j bridge was installed
    BusinessLogic.julLog(julLogger);
    BusinessLogic.slf4jLog(logger);
}

在此代码中,我们首先删除所有现有的 JUL 处理程序以防止重复的日志条目。然后,我们安装 SLF4J 桥接器,它将所有 JUL 日志记录重定向到 SLF4J。

使用logging.properties进行配置
或者,我们可以使用 JUL 的logging.properties文件以声明方式配置桥接器。此方法非常适合无法修改代码库或添加编程配置的应用程序。

为此,我们首先创建src/main/resources/logging.properties并指定 JUL 处理程序和日志记录级别:

handlers = org.slf4j.bridge.SLF4JBridgeHandler
.level = INFO

在此配置中:

  • handlers行指定SLF4JBridgeHandler应该处理 JUL 根记录器的日志记录
  • .level属性将默认日志记录级别设置为INFO,这意味着只会记录此级别或更高级别的消息
JUL 默认使用 JAVA_HOME/conf目录 中的 logging.properties文件。为了确保使用我们的自定义配置,我们需要在运行应用程序时明确指定logging.properties文件的位置。这可以使用JVM参数来完成:

-Djava.util.logging.config.file=/path/to/logging.properties

这确保加载我们的自定义日志配置而不是默认配置。

请注意,这种方法效果很好,但它的缺点是我们需要管理一些 JUL 内部组件才能使其工作。编程方法允许我们坚持使用 SLF4J API 进行配置。

测试日志配置
现在我们已经设置了 JUL 到 SLF4J 桥接器,我们可以测试我们的日志配置以确保一切按预期工作。

1. 通过 JUL 记录
即使第三方库使用 JUL 记录日志,日志消息现在也将通过 SLF4J 路由:

import java.util.logging.Logger;
public class BusinessLogic {
    static void julLog(Logger julLogger) {
        julLogger.info("This is a JUL info log message!");
        julLogger.warning("This is a JUL warning log message!");
    }
}

这些日志将由 SLF4J 后端(例如 slf4j-simple)而不是 JUL 处理,并且julLog()输出类似于:

[main] INFO com.jdon.JULAppWithProgrammaticSLF4J - This is a JUL info log message!
[main] WARN com.jdon.JULAppWithProgrammaticSLF4J - This is a JUL warning log message!

2. 通过 SLF4J 直接记录
虽然上一节显示我们现在可以将 JUL 与 SLF4J 一起使用,但最好声明一个 SLF4J 记录器:

public class BusinessLogic {
    static void slf4jLog(org.slf4j.Logger logger) {
        logger.info("This is an SLF4J info log message!");
        logger.error("This is an SLF4J error log message!");
    }
}

通过测试基于 JUL 和基于 SLF4J 的日志记录,我们可以确认集成正常运行。