【Java包系列】Java SLF4J由来场景用法示例

【Java包系列】Java SLF4J由来场景用法示例

1. 由来

Simple Logging Facade for Java (SLF4J) 是一种用于各种日志框架的简单门面或抽象。支持的日志框架包括 java.util.logging、log4j 1.x、reload4j 和 logback。在部署时,SLF4J 允许用户选择所需的日志框架。需要注意的是,启用 SLF4J 的库/应用程序只需要添加一个必需的依赖项,即 slf4j-api-2.0.10.jar。

2. 适用场景

SLF4J适用于任何Java项目,特别是那些需要进行日志记录和调试的应用程序。它提供了一个抽象的日志接口,使得开发人员可以轻松地切换不同的日志实现,如Logback、Log4j等,而无需修改代码。

3. 多种主要实现用法及其代码示例

3.1 配置和初始化

首先,需要添加相应的SLF4J实现库到项目的依赖中。然后,在代码中配置和初始化SLF4J。

依赖文件

gradle
  compile 'log4j:log4j:1.2.17'
  compile 'org.slf4j:slf4j-log4j12:1.7.21'
  compile 'org.slf4j:slf4j-api:1.7.21'
maven
 <dependency>
	<groupId>log4j</groupId>
	<artifactId>log4j</artifactId>
	<version>1.2.17</version>
</dependency>
<dependency>
	<groupId>org.slf4j</groupId>
	<artifactId>slf4j-log4j12</artifactId>
	<version>1.7.21</version>
</dependency>

<dependency>
	<groupId>org.slf4j</groupId>
	<artifactId>slf4j-api</artifactId>
	<version>1.7.21</version>
</dependency>
配置文件log4j
### set log levels ###
log4j.rootLogger = INFO,root,stdout

log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.conversionPattern=%d{yyyy-MM-dd HH:mm:ss.SSSXXX} %-5p [%t] [%C %L] %m%n

log4j.appender.root.Append=true
log4j.appender.root.File=${scheduleProject}logs/root.log
log4j.appender.root.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss.SSSXXX} %-5p [%t] [%C %L] %m%n
log4j.appender.root.layout=org.apache.log4j.PatternLayout
log4j.appender.root.MaxBackupIndex=50
log4j.appender.root.MaxFileSize=20MB
log4j.appender.root=org.apache.log4j.RollingFileAppender
log4j.appender.root.zipPermission=400
log4j.appender.root.logPermission=600

Hello World

按照编程传统,在这里我们提供一个示例,演示使用 SLF4J 输出 “Hello world” 的最简单方式。首先通过名称 “HelloWorld” 获取一个记录器(Logger),然后使用该记录器将消息 “Hello World” 记录下来。

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class HelloWorld {

  public static void main(String[] args) {
    Logger logger = LoggerFactory.getLogger(HelloWorld.class);
    logger.info("Hello World");
  }
}

要运行此示例,首先需要获取 slf4j 构件。完成后,将 slf4j-api-2.0.10.jar 文件添加到类路径中。

编译并运行 HelloWorld 将在控制台上打印以下输出:

SLF4J: No SLF4J providers were found.
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See [https://www.slf4j.org/codes.html#noProviders](https://www.slf4j.org/codes.html#noProviders) for further details.

如果使用的是 SLF4J 1.7 或更早版本,则消息将为:

SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See [https://www.slf4j.org/codes.html#StaticLoggerBinder](https://www.slf4j.org/codes.html#StaticLoggerBinder) for further details.

此警告是因为在类路径上找不到 slf4j 提供程序(或绑定)。

只要在类路径上添加提供程序,警告就会消失。假设您添加了 slf4j-simple-2.0.10.jar,使得类路径包含以下内容:

slf4j-api-2.0.10.jar
slf4j-simple-2.0.10.jar

现在编译并运行 HelloWorld 将在控制台上输出以下内容:

0 [main] INFO HelloWorld - Hello World

3.2 参数化日志消息

SLF4J支持参数化日志消息,以提高性能并减少字符串拼接的开销。

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class ParameterizedLoggingExample {
    private static final Logger logger = LoggerFactory.getLogger(ParameterizedLoggingExample.class);

    public static void main(String[] args) {
        String name = "John";
        int age = 25;
        
        // 使用参数化日志消息
        logger.info("User {} is {} years old", name, age);
    }
}

4. 其他类似包

除了SLF4J之外,还有一些类似的Java日志库可以考虑使用,如Apache Log4j、Java Util Logging(JUL)等。SLF4J是一个日志门面库,它提供了一个抽象的日志接口,而不是具体的日志实现。与其他类似的包相比,SLF4J的设计目标是简单和易用,它提供了一套统一的日志API,使得开发人员可以方便地切换不同的日志实现,并且减少对底层日志库的直接依赖。

5.SLF4J 用户手册

流畅的日志记录 API

自 SLF4J 2.0.0 版本开始,SLF4J API 引入了向后兼容的流畅日志记录 API,并且需要 Java 8。向后兼容意味着现有的日志框架无需更改即可从流畅日志记录 API 中受益。

通过 LoggingEventBuilder 逐步构建日志事件,并在完全构建完成后进行日志记录。org.slf4j.Logger 接口中新增的 atTrace()、atDebug()、atInfo()、atWarn() 和 atError() 方法返回一个 LoggingEventBuilder 实例。对于禁用的日志级别,返回的 LoggingEventBuilder 实例不执行任何操作,以保持传统日志接口的性能优势。

使用流畅 API 时,必须通过调用 log() 方法变体之一来终止方法链。如果忘记调用任何 log() 方法变体,则不会进行日志记录,无论日志级别如何。幸运的是,如果出现这种情况,某些 IDE 可以通过编译器警告提醒您。

以下是一些用法示例:

语句

logger.atInfo().log("Hello world.");

等价于:

logger.info("Hello world.");

以下日志语句在输出结果上是等效的(对于默认实现):

int newT = 15;
int oldT = 16;

// 使用传统 API
logger.debug("Temperature set to {}. Old value was {}.", newT, oldT);

// 使用流畅 API,带参数的日志消息
logger.atDebug().log("Temperature set to {}. Old value was {}.", newT, oldT);
   
// 使用流畅 API,逐个添加参数,然后进行日志记录
logger.atDebug().setMessage("Temperature set to {}. Old value was {}.").addArgument(newT).addArgument(oldT).log();

// 使用流畅 API,使用 Supplier 添加一个参数,然后带有另一个参数进行日志记录
// 假设 t16() 方法返回 16
logger.atDebug().setMessage("Temperature set to {}. Old value was {}.").addArgument(() -> t16()).addArgument(oldT).log();

流畅的日志记录 API 允许以多种不同类型的数据规范 org.slf4j.Logger,而无需为 Logger 接口中的方法数量组合爆炸。

现在可以传递多个标记(Markers)、使用 Supplier 传递参数或传递多个键值对。键值对在与可以自动解析它们的日志数据分析器结合使用时特别有用。

以下日志语句是等效的:

int newT = 15;
int oldT = 16;

// 使用传统 API
logger.debug("oldT={} newT={} Temperature changed.", oldT, newT);

// 使用流畅 API 
logger.atDebug().setMessage("Temperature changed.").addKeyValue("oldT", oldT).addKeyValue("newT", newT).log();

键值对变体的 API 将键值对作为单独的对象存储。当前在 org.slf4j.Logger 类中的默认实现将键值对前缀添加到消息中。日志后端可以自由选择并鼓励提供更可定制的行为。

在部署时与日志框架关联

正如前面提到的,SLF4J 旨在作为各种日志框架的门面。SLF4J 发行版附带了几个称为 “providers” 的 jar 文件,每个提供程序对应一个支持的日志框架。需要注意的是,SLF4J 版本 1.7 及之前使用 “binding” 一词表示提供程序。

以下是一些提供程序的部分(非详尽)列表。

  • slf4j-log4j12-2.0.10.jar:用于 log4j 1.x 的绑定/提供程序,这是一个广泛使用的日志框架。
  • slf4j-reload4j-2.0.10.jar:SINCE 1.7.33 reload4j 框架的绑定/提供程序。
  • slf4j-jdk14-2.0.10.jar:java.util.logging 的绑定/提供程序,也称为 JDK 1.4 日志记录。
  • slf4j-nop-2.0.10.jar:NOP 的绑定/提供程序,静默丢弃所有日志记录。
  • slf4j-simple-2.0.10.jar:Simple 实现的绑定/提供程序,将所有事件输出到 System.err。只打印级别为 INFO 或更高的消息。
  • slf4j-jcl-2.0.10.jar:Apache Commons Logging 的绑定/提供程序。

本机实现 还有一些不属于 SLF4J 项目的 SLF4J 绑定/提供程序,例如原生实现的 logback。Logback 的 ch.qos.logback.classic.Logger 类是 SLF4J 的 org.slf4j.Logger 接口的直接实现。因此,在与 logback 结合使用 SLF4J 时,严格来说没有内存和计算开销。

要切换日志框架,只需替换类路径上的 slf4j 绑定。例如,要从 java.util.logging 切换到 reload4j,请将 slf4j-jdk14-2.0.10.jar 替换为 slf4j-reload4j-2.0.10.jar。

SINCE 2.0.0 从 2.0.0 版本开始,SLF4J 绑定被称为提供程序。然而,总体思路保持不变。SLF4J API 版本 2.0.0 使用 ServiceLoader 机制来查找其日志后端。

广泛分发的组件和库的作者可以使用 SLF4J 接口来避免强制用户使用特定的日志框架。因此,用户可以在部署时选择所需的日志框架,只需将相应的 slf4j 绑定插入到类路径中,稍后可以替换现有的绑定并重新启动应用程序来更改日志框架。这种方法已经被证明非常简单和非常可靠。

从 SLF4J 版本 1.6.0 开始,如果在类路径上找不到任何绑定,那么 slf4j-api 将默认为一个丢弃所有日志请求的无操作实现。因此,与其因为缺少 org.slf4j.impl.StaticLoggerBinder 类而抛出 NoClassDefFoundError,SLF4J 版本 1.6.0 及更高版本将发出一条关于缺少绑定的警告消息,并继续丢弃所有日志请求而不再抗议。例如,让 Wombat 成为依赖于 SLF4J 进行日志记录的某个与生物学相关的框架。为了避免对最终用户强制使用日志框架,Wombat 的分发包括 slf4j-api.jar,但不包含绑定。即使在类路径上没有任何 SLF4J 绑定的情况下,Wombat 的分发包仍然可以立即使用,而无需用户从 SLF4J 的网站下载绑定。只有当用户决定启用日志记录时,才需要安装与她选择的日志框架相对应的 SLF4J 绑定。

基本规则

嵌入组件(如库或框架)不应声明对任何 SLF4J 绑定/提供程序的依赖,而只应依赖于 slf4j-api。当库对特定绑定声明传递性依赖项时,这个绑定就会被强加给最终用户,从而否定了 SLF4J 的目的。请注意,声明非传递性依赖项(例如用于测试的依赖项)对绑定没有影响,不会影响最终用户。

关于嵌入组件中的 SLF4J 使用,也可以在与日志配置、依赖项减少和测试相关的 FAQ 中进行讨论。

为日志声明项目依赖关系

考虑到 Maven 的传递性依赖规则,对于“常规”项目(非库或框架),可以通过单个依赖声明来声明日志依赖关系。

SLF4J API

SLF4J API 包含在 “org.slf4j:slf4j-api” 构件中。您可以在 pom.xml 文件中显式声明对它的依赖关系,如下所示。请注意,大多数日志实现会自动将 slf4j-api 作为依赖项引入。然而,通常建议显式声明对 slf4j-api 的依赖关系,以通过 Maven 的 “最近定义” 依赖决策规则确定项目中 slf4j-api 的正确版本。

<dependency> 
  <groupId>org.slf4j</groupId>
  <artifactId>slf4j-api</artifactId>
  <version>2.0.10</version>
</dependency>

LOGBACK-CLASSIC 1.3.X(JAVAX EE)

如果您希望使用 logback-classic 作为 Javax EE 的底层日志框架,只需在 pom.xml 文件中声明 “ch.qos.logback:logback-classic” 作为依赖项,如下所示。除了 logback-classic-1.3.6.jar,这还会将 slf4j-api-2.0.10.jar 和 logback-core-1.3.6.jar 引入到项目中。请注意,显式声明对 logback-core-1.3.6 或 slf4j-api-2.0.10.jar 的依赖关系是正确的,并且可能需要通过 Maven 的 “最近定义” 依赖决策规则来确定所述构件的正确版本。

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

LOGBACK-CLASSIC 1.4.X(JAKARTA EE)

如果您希望使用 logback-classic 作为 Jakarta EE 的底层日志框架,只需在 pom.xml 文件中声明 “ch.qos.logback:logback-classic” 作为依赖项,如下所示。除了 logback-classic-1.4.6.jar,这还会将 slf4j-api-2.0.10.jar 和 logback-core-1.4.6.jar 引入到项目中。请注意,显式声明对 logback-core-1.4.6 或 slf4j-api-2.0.10.jar 的依赖关系是正确的,并且可能需要通过 Maven 的 “最近定义” 依赖决策规则来确定所述构件的正确版本。

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

RELOAD4J

如果您希望使用 reload4j 作为底层日志框架,只需在 pom.xml 文件中声明 “org.slf4j:slf4j-reload4j” 作为依赖项,如下所示。除了 slf4j-reload4j-2.0.10.jar,这还会将 slf4j-api-2.0.10.jar 和 reload4j-1.2.25.jar 引入到项目中。请注意,显式声明对 reload4j-1.2.25.jar 或 slf4j-api-2.0.10.jar 的依赖关系是正确的,并且可能需要通过 Maven 的 “最近定义” 依赖决策规则来确定所述构件的正确版本。

<dependency> 
  <groupId>org.slf4j</groupId>
  <artifactId>slf4j-reload4j</artifactId>
  <version>2.0.10</version>
</dependency>

LOG4J 1.2.X

自 SLF4J 版本 1.7.36 开始,对 org.slf4j:slf4j-log4j12 的依赖会根据 Maven 的 <relocation> 指令重定向到 org.slf4j:slf4j-reload4j。

<dependency> 
  <groupId>org.slf4j</groupId>
  <artifactId>slf4j-log4j12</artifactId>
  <version>2.0.10</version>
</dependency>

JAVA.UTIL.LOGGING

如果您希望使用 java.util.logging 作为底层日志框架,只需在 pom.xml 文件中声明 “org.slf4j:slf4j-jdk14” 作为依赖项,如下所示。除了 slf4j-jdk14-2.0.10.jar,这还会将 slf4j-api-2.0.10.jar 引入到项目中。请注意,显式声明对 slf4j-api-2.0.10.jar 的依赖关系是正确的,并且可能需要通过 Maven 的 “最近定义” 依赖决策规则来确定所述构件的正确版本。

<dependency> 
  <groupId>org.slf4j</groupId>
  <artifactId>slf4j-jdk14</artifactId>
  <version>2.0.10</version>
</dependency>

SLF4J SIMPLE

如果您希望使用 org.slf4j.simple 作为底层日志实现,只需在 pom.xml 文件中声明 “org.slf4j:slf4j-simple” 作为依赖项,如下所示。除了 slf4j-simple-2.0.10.jar,这还会将 slf4j-api-2.0.10.jar 引入到项目中。请注意,显式声明对 slf4j-api-2.0.10.jar 的依赖关系是正确的,并且可能需要通过 Maven 的 “最近定义” 依赖决策规则来确定所述构件的正确版本。

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

二进制兼容性

SLF4J 提供程序/绑定指的是用于将 SLF4J 绑定到底层日志框架(例如 java.util.logging 或 reload4j)的构件,如 slf4j-jdk14.jar 或 slf4j-reload4j.jar。

从客户端的角度来看,slf4j-api,特别是 org.slf4j 包中的类,在所有版本上都是向后兼容的。使用 slf4j-api-N.jar 编译的客户端代码将与任何 N 和 M 的 slf4j-api-M.jar 完全正常地运行。您只需要确保提供程序/绑定的版本与 slf4j-api.jar 的版本匹配。您不必担心项目中给定依赖项使用的 slf4j-api.jar 的版本。

混合使用不同版本的 slf4j-api.jar 和 SLF4J 提供程序/绑定可能会导致问题。例如,如果您使用的是 slf4j-api-2.0.10.jar,则应该同时使用 slf4j-simple-2.0.10.jar,而使用 slf4j-simple-1.5.5.jar 将无法工作。

然而,从客户端的角度来看,SLF4J API,特别是 org.slf4j 包中的类,在所有版本上都是向后兼容的。使用 slf4j-api-N.jar 编译的客户端代码将与任何 N 和 M 的 slf4j-api-M.jar 完全正常地运行。您只需要确保提供程序/绑定的版本与 slf4j-api.jar 的版本匹配。您不必担心项目中给定依赖项使用的 slf4j-api.jar 的版本。您可以始终使用任何版本的 slf4j-api.jar,只要 slf4j-api.jar 的版本与其提供程序/绑定的版本匹配,就应该没问题。

通过 SLF4J 进行日志记录
通常情况下,一个项目可能依赖于多个组件,这些组件依赖于除 SLF4J 之外的各种日志 API。常见的情况是项目依赖于 JCL、java.util.logging、log4j 和 SLF4J 的组合。然后,通过单一渠道整合日志记录变得非常有必要。SLF4J 可以满足这种常见用例,通过提供用于 JCL、java.util.logging 和 log4j 的桥接模块来实现。有关更多详细信息,请参阅 Bridging legacy APIs 页面。

支持 JDK 平台日志(JEP 264)SLF4J

自 SLF4J 2.0.0-ALPHA5 开始,slf4j-jdk-platform-logging 模块添加了对 JDK 平台日志的支持。

<dependency> 
  <groupId>org.slf4j</groupId>
  <artifactId>slf4j-jdk-platform-logging</artifactId>
  <version>2.0.10</version>
</dependency>

Mapped Diagnostic Context (MDC) 支持

“Mapped Diagnostic Context” 是日志框架维护的一个映射,应用程序代码在其中提供键值对,然后日志框架将这些信息插入到日志消息中。MDC 数据在过滤消息或触发某些操作时也非常有帮助。

SLF4J 支持 MDC(Mapped Diagnostic Context)。如果底层日志框架提供了 MDC 功能,则 SLF4J 将委托给底层框架的 MDC。需要注意的是,目前只有 log4j 和 logback 提供了 MDC 功能。如果底层框架不提供 MDC,例如 java.util.logging,则 SLF4J 仍然会存储 MDC 数据,但其中的信息需要由自定义用户代码来检索。

因此,作为 SLF4J 用户,您可以在 log4j 或 logback 存在的情况下利用 MDC 信息,而无需强制用户将这些日志框架作为依赖项。

有关 MDC 的更多信息,请参阅 logback 手册中的 MDC 章节。

总结

SLF4J 是一种用于各种日志框架的简单门面或抽象,它具有以下优势:

  • 日志 API 与配置的分离
  • 在部署时选择日志框架
  • 快速失败操作
  • 支持流行的日志框架
  • 桥接传统的日志 API
  • 迁移源代码使用 slf4j-migrator 实用程序
  • 支持参数化日志消息

SLF4J 提供了多种绑定/提供程序,可以根据需要选择与底层日志框架的适配器。嵌入组件应该依赖于 slf4j-api 而不是具体的绑定/提供程序,并且库的作者应该避免强制用户使用特定的日志框架。

SLF4J 还支持各种功能,如 Mapped Diagnostic Context (MDC),以提供更灵活和强大的日志记录能力。

对于 SLF4J 的使用,建议在项目中显式声明对 slf4j-api 的依赖关系,并根据需要添加相应的绑定/提供程序。

6. 官方链接

SLF4J官方网站

手册

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

BigDataMLApplication

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值