1. 概述

简单Java日志门面(简称SLF4J)作为不同日志框架(如java.util.logging, logback, Log4j)的外观模式。它提供了一个通用的API,使得日志独立于实际实现,从而支持多种框架共存、迁移以及提供标准化的接口和一些“语法糖”。

本教程将讨论如何与Log4j、Logback、Log4j 2和Jakarta Commons Logging集成SLF4J所需的依赖和配置。

有关这些实现的更多信息,请参阅我们的文章:Java日志入门

2. Log4j 2 配置

要使用SLF4J与Log4j 2集成,我们需要在pom.xml中添加以下库:

<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-api</artifactId>
    <version>2.7</version>
</dependency>
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-core</artifactId>
    <version>2.7</version>
</dependency>
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-slf4j-impl</artifactId>
    <version>2.7</version>
</dependency>

最新版本可以在以下链接找到:log4j-apilog4j-corelog4j-slf4j-impl

实际的日志配置遵循Log4j 2的原生配置。

让我们看看如何创建Logger实例:

public class Slf4jExample {

    private static Logger logger = LoggerFactory.getLogger(Slf4jExample.class);

    public static void main(String[] args) {
        logger.debug("Debug log message");
        logger.info("Info log message");
        logger.error("Error log message");
    }
}

请注意,LoggerLoggerFactory来自org.slf4j包。

有关此配置示例项目的运行,请查看这里

3. Logback 配置

由于Logback本身已经使用了SLF4J,所以我们不需要将SLF4J添加到类路径中以使用它。Logback是SLF4J的参考实现。

因此,我们只需要包含Logback库:

<dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-classic</artifactId>
    <version>1.2.6</version>
</dependency>

最新版本可以在logback-classic找到。

配置是Logback特有的,但与SLF4J无缝协作。有了适当的依赖和配置,我们可以使用之前章节中的相同代码来处理日志。

4. Log4j 配置

在前面的章节中,我们探讨了SLF4J“置于”特定日志实现之上的用法,它完全隐藏了底层框架。有时,由于第三方要求,我们无法替换现有的日志解决方案,但这并不限制项目只使用已有的框架。

我们可以配置SLF4J作为桥接器,将对现有框架的调用重定向到它。

让我们添加必要的依赖以建立Log4j的桥接:

<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>log4j-over-slf4j</artifactId>
    <version>1.7.30</version>
</dependency>

有了这个依赖(请检查log4j-over-slf4j的最新版本),所有对Log4j的调用都将被重定向到SLF4J。

请参阅官方文档了解如何桥接现有框架的更多内容。

如同其他框架一样,Log4j也可以作为底层实现。

让我们添加必要的依赖:

<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-log4j12</artifactId>
    <version>1.7.30</version>
</dependency>
<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>

这些最新的版本可以在slf4j-log4j12log4j中找到。配置为此方式的示例项目可在这里找到。

5. JCL桥接器配置

在前面的章节中,我们展示了如何使用相同的代码库支持不同实现的日志。这是SLF4J的主要承诺和优势,也是Jakarta Commons Logging(或Apache Commons Logging,简称JCL)背后的目标。

JCL旨在作为一个类似于SLF4J的框架。主要区别在于,JCL在运行时通过类加载系统动态确定底层实现。在存在自定义类加载器的情况下,这种方法可能存在问题。

SLF4J在编译时解决绑定,被认为是更简单但功能强大的。

幸运的是,两个框架可以在桥接模式下协同工作:

<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>jcl-over-slf4j</artifactId>
    <version>1.7.30</version>
</dependency>

最新的依赖版本可以在jcl-over-slf4j找到。

与其他情况一样,相同的代码库将运行良好。

6. SLF4J 的额外功能

SLF4J提供了额外的功能,可以使日志更高效,代码更易读。

例如,SLF4J提供了非常有用的参数工作接口:

String variable = "Hello John";
logger.debug("Printing variable value: {}", variable);

以下是Log4j做同样事情的代码:

String variable = "Hello John";
logger.debug("Printing variable value: " + variable);

如我们所见,Log4j会在任何情况下将String拼接起来。在高负载应用中,这可能导致性能问题。相反,SLF4J只有在debug级别启用时才会拼接String

要在Log4j中实现相同功能,我们需要添加一个额外的if块,检查debug级别是否启用:

String variable = "Hello John";
if (logger.isDebugEnabled()) {
    logger.debug("Printing variable value: " + variable);
}

SLF4J标准化了日志级别,这些级别在特定实现中有所不同。它移除了Log4j引入的FATAL级别,理由是在日志框架中,我们不应该决定何时终止应用程序。

使用的日志级别是ERRORWARNINFODEBUGTRACE。有关更多使用信息,请参阅我们的文章:Java日志入门

7. 总结

SLF4J有助于在不发出声音的情况下在日志框架之间切换。它简单而灵活,可以提高可读性和性能。

如往常一样,代码可在GitHub上找到。此外,我们还引用了一个专注于不同文章的项目,其中包含了讨论过的日志配置,可在这里找到。