Java的简单日志Facade (SLF4J)作为各种日志框架(例如Java .util)的简单外观或抽象。Logging, logback, log4j)允许最终用户在部署时插入所需的日志框架。

在开始使用SLF4J之前,我们强烈建议您阅读两页的SLF4J用户手册。

注意,启用slf4j的库意味着只添加一个强制依赖项,即slf4j-api.jar。如果在类路径上没有找到绑定,那么SLF4J将默认为无操作实现。

如果您希望将Java源文件迁移到SLF4J,请考虑我们的迁移工具,它可以在几分钟内将您的项目迁移到使用SLF4J API。

如果您所依赖的外部维护组件使用的不是SLF4J的日志API,比如commons logging、log4j或java.util。日志,看看SLF4J对遗留api的二进制支持。

SLF4J user manual

Java的简单日志Facade (SLF4J)充当各种日志框架(如Java .util)的简单外观或抽象。logging、logback和log4j。SLF4J允许终端用户在部署时插入所需的日志记录框架。注意,启用slf4j的库/应用程序意味着只增加一个强制性依赖项,即slf4j-api-${project.version}.jar。

  • 如果在类路径上没有找到绑定,那么SLF4J将默认为无操作实现。
  • Logger界面中的打印方法现在提供接受varargs而不是Object[]的变体。这个更改意味着SLF4J需要JDK 1.5或更高版本。在幕后,Java编译器将方法中的varargs部分转换为Object[]。因此,由编译器生成的Logger接口在1.7中没有区别, 从1.6开始。它遵循SLF4J版本1.7。SLF4J version 1.6.x是完全100%的no-if -or-but兼容。
  • 记录器检索时间的显著改进。鉴于改进的程度,我们强烈建议用户迁移到SLF4J 1.7.5或更高版本。
  • SLF4J API版本2.0.0需要Java 8,并引入了向后兼容的流畅日志记录API。通过向后兼容,我们的意思是,为了让用户从流畅的日志记录API中受益,不必更改现有的日志记录框架。
  • SLF4J API版本2.0.0依赖于ServiceLoader机制来查找日志记录后端。更多细节请参见相关FAQ条目。

Hello World

按照编程传统的习惯,这里有一个示例说明使用SLF4J输出“Hello world”的最简单方法。它首先获取一个名为“HelloWorld”的日志记录器。该日志记录器依次用于记录消息“Hello World”。

  1. import org.slf4j.Logger;
  2. import org.slf4j.LoggerFactory;
  3. public class HelloWorld {
  4. public static void main(String[] args) {
  5. Logger logger = LoggerFactory.getLogger(HelloWorld.class);
  6. logger.info("Hello World");
  7. }
  8. }

要运行这个示例,首先需要下载slf4j发行版,然后解压缩它。完成之后,将文件slf4j-api-${latest.stable.version}.jar添加到类路径中。

编译和运行HelloWorld将在控制台打印以下输出。

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

打印此警告是因为在类路径上找不到slf4j绑定。
当您将绑定添加到类路径时,警告将立即消失。假设你添加了slf4j-simple-${latest.stable.version}.jar,这样你的类路径就包含:

  • slf4j-api-${latest.stable.version}.jar
  • slf4j-simple-${latest.stable.version}.jar

编译和运行HelloWorld现在将在控制台上得到以下输出。

Typical usage pattern

下面的示例代码说明了SLF4J的典型使用模式。注意第15行中占位符{}的使用。更多细节请参见FAQ中的问题“什么是最快的日志记录方式?”

  1. 1: import org.slf4j.Logger;
  2. 2: import org.slf4j.LoggerFactory;
  3. 3:
  4. 4: public class Wombat {
  5. 5:
  6. 6: final Logger logger = LoggerFactory.getLogger(Wombat.class);
  7. 7: Integer t;
  8. 8: Integer oldT;
  9. 9:
  10. 10: public void setTemperature(Integer temperature) {
  11. 11:
  12. 12: oldT = t;
  13. 13: t = temperature;
  14. 14:
  15. 15: logger.debug("Temperature set to {}. Old temperature was {}.", t, oldT);
  16. 16:
  17. 17: if(temperature.intValue() > 50) {
  18. 18: logger.info("Temperature has risen above 50 degrees.");
  19. 19: }
  20. 20: }
  21. 21: }

Fluent Logging API

由于2.0.0 SLF4J API版本2.0.0需要Java 8,并引入了向后兼容的流畅日志记录API。通过向后兼容,我们的意思是,为了让用户从流畅的日志记录API中受益,不必更改现有的日志记录框架。

其思想是使用LoggingEventBuilder一块一块地构建日志事件,并在事件完全构建完成后进行日志记录。atTrace()、atDebug()、atInfo()、atWarn()和atError()方法都是org.slf4j中的新方法。Logger接口,返回一个LoggingEventBuilder实例。对于禁用的日志级别,返回的LoggingEventBuilder实例什么也不做,因此保留了传统日志接口的纳秒级性能。

以下是一些用法示例:
logger.atInfo().log(“Hello world”);
logger.info(“Hello world.”);

下面的日志语句在它们的输出中是等效的(对于默认实现):

  1. int newT = 15;
  2. int oldT = 16;
  3. // using traditional API
  4. logger.debug("Temperature set to {}. Old temperature was {}.", newT, oldT);
  5. // using fluent API, add arguments one by one and then log message
  6. logger.atDebug().addArgument(newT).addArgument(oldT).log("Temperature set to {}. Old temperature was {}.");
  7. // using fluent API, log message with arguments
  8. logger.atDebug().log("Temperature set to {}. Old temperature was {}.", newT, oldT);
  9. // using fluent API, add one argument and then log message providing one more argument
  10. logger.atDebug().addArgument(newT).log("Temperature set to {}. Old temperature was {}.", oldT);
  11. // using fluent API, add one argument with a Supplier and then log message with one more argument.
  12. // Assume the method t16() returns 16.
  13. logger.atDebug().addArgument(() -> t16()).log(msg, "Temperature set to {}. Old temperature was {}.", oldT);

流畅的日志记录API允许向org.slf4j规范许多不同类型的数据。Logger接口中的方法数量不会出现组合爆炸。

现在可以传递多个标记、通过Supplier传递参数或传递多个键值对。键值对与可以自动解释它们的日志数据分析器一起使用时特别有用。

  1. int newT = 15;
  2. int oldT = 16;
  3. // using classical API
  4. logger.debug("oldT={} newT={} Temperature changed.", newT, oldT);
  5. // using fluent API
  6. logger.atDebug().addKeyValue("oldT", oldT).addKeyValue("newT", newT).log("Temperature changed.");

API的键值对变体将键值对存储为分离对象。默认实现将键值对写在message的前面。日志后端是免费的,甚至鼓励提供更可定制的行为。

在部署时绑定日志框架

如前所述,SLF4J支持各种日志记录框架。SLF4J发行版附带了几个称为“SLF4J绑定”的jar文件,每个绑定对应于一个受支持的框架。

  • slf4j-log4j12-${latest.stable.version}.jar _Binding for log4j version 1.2, a widely used logging framework. You also need to place _log4j.jar on your class path.
  • _slf4j-jdk14-${latest.stable.version}.jar _Binding for java.util.logging, also referred to as JDK 1.4 logging
  • _slf4j-nop-${latest.stable.version}.jar _Binding for NOP, silently discarding all logging.
  • slf4j-simple-${latest.stable.version}.jar 为Simple实现绑定,它将输出所有事件到System.err。只打印INFO级别及更高级别的消息。这种绑定在小型应用程序上下文中可能很有用。
  • _slf4j-jcl-${latest.stable.version}.jar _Binding for Jakarta Commons Logging. This binding will delegate all SLF4J logging to JCL.
  • logback-classic-${logback.version}.jar (requires logback-core-${logback.version}.jar) 在SLF4J项目的外部也有SLF4J绑定,例如logback本地实现了SLF4J。Logback’s ch.qos.logback.classic.Logger class is a direct implementation of SLF4J’s org.slf4j.Logger interface. 因此,将SLF4J与logback结合使用只涉及零内存和计算开销。

要切换日志记录框架,只需替换类路径上的slf4j绑定。例如,要从java.util.logging切换到log4j,只需用slf4j-log4j12-${latest.stable.version}.jar替换slf4j-jdk14-${latest.stable.version}.jar

SLF4J不依赖于任何特殊的类装入器。事实上,每个SLF4J绑定都是在编译时硬连接的,以使用且仅使用一个特定的日志记录框架。例如,slf4j-log4j12-${latest.stable.version}.jar绑定在编译时被绑定为使用log4j。在您的代码中,除了slf4j-api-${latest.stable.version}.jar之外,您只需将一个且仅一个您所选择的绑定放到适当的类路径位置。不要在类路径上放置多个绑定。这里有一个图解说明了总体思想。

SLF4J接口及其各种适配器非常简单。大多数熟悉Java语言的开发人员应该能够在不到一小时的时间内阅读并完全理解代码。不需要了解类装入器,因为SLF4J既不使用也不直接访问任何类装入器。因此,SLF4J不存在Jakarta Commons Logging (JCL)中观察到的类装入器问题或内存泄漏。

鉴于SLF4J接口及其部署模型的简单性,新的日志记录框架的开发人员应该会发现编写SLF4J绑定非常容易。

Libraries

广泛分布的组件和库的作者可能会针对SLF4J接口编写代码,以避免将日志记录框架强加给他们的最终用户。因此,最终用户可以在部署时通过在类路径上插入相应的slf4j绑定来选择所需的日志记录框架,稍后可以通过用类路径上的另一个绑定替换现有的绑定并重新启动应用程序来更改该绑定。这种方法已经被证明是简单且非常健壮的。

从SLF4J 1.6.0版本开始,如果在类路径上没有找到绑定,那么SLF4J -api将默认为无操作实现,丢弃所有日志请求。Thus, instead of throwing a NoClassDefFoundError because the org.slf4j.impl.StaticLoggerBinder class is missing, SLF4J版本1.6.0及更高版本将发出一条关于没有绑定的警告消息,并继续丢弃所有日志请求,而没有进一步的抗议。例如,让Wombat成为依赖于SLF4J进行日志记录的生物相关框架。为了避免将日志框架强加给终端用户,Wombat的发行版包含了slf4j-api.jar,但没有绑定。即使在类路径上没有任何SLF4J绑定,Wombat的发行版仍然可以开箱即用,并且不需要终端用户从SLF4J的网站下载绑定。只有当最终用户决定启用日志记录时,她才需要安装与她选择的日志记录框架相对应的SLF4J绑定。

像库或框架这样的嵌入式组件不应该声明对任何SLF4J绑定的依赖,而应该只依赖于SLF4J -api。当库声明对特定绑定的可传递依赖时,该绑定将被强加给最终用户,从而否定了SLF4J的用途。注意,对绑定声明非传递依赖(例如用于测试)不会影响最终用户。

FAQ中还讨论了SLF4J在嵌入式组件中的使用与日志配置、依赖性降低和测试的关系。

为日志声明项目依赖项

根据Maven的传递依赖规则,对于“常规”项目(不是库或框架),可以通过单个依赖声明来声明日志依赖。

LOGBACK-CLASSIC
如果您希望使用logback-classic作为底层日志记录框架,您所需要做的就是声明“ch.qos。Logback: Logback -classic”作为pomc .xml文件中的一个依赖项,如下所示。除了logback-classic-${logback.version}.jar,这将把slf4j-api-${latest.stable.version}.jar和logback-core-${logback.version}.jar拉到你的项目中。注意,显式声明对logback-core的依赖-${logback。${latest.stable.version}或slf4j-api-${latest.stable.version}.jar是正确的,并且可能需要通过Maven的“最近定义”依赖仲裁规则来强制执行所提到的工件的正确版本。

  1. <dependency>
  2. <groupId>ch.qos.logback</groupId>
  3. <artifactId>logback-classic</artifactId>
  4. <version>${logback.version}</version>
  5. </dependency>

LOG4J
如果您希望使用log4j作为底层日志记录框架,您所需要做的就是声明“org.slf4j:slf4j-log4j12”。作为您的pom.xml文件中的依赖项,如下所示。除了slf4j-log4j12-${latest.stable.version}.jar,这将把slf4j-api-${latest.stable.version}.jar和log4j-${log4j.version}.jar拉到你的项目中。注意,显式声明对log4j-${log4j.version}.jar或slf4j-api-${latest.stable.version}.jar的依赖是正确的,并且可能需要通过Maven的“最近定义”依赖中介规则来强制执行所提到的工件的正确版本。

  1. <dependency>
  2. <groupId>org.slf4j</groupId>
  3. <artifactId>slf4j-log4j12</artifactId>
  4. <version>${latest.stable.version}</version>
  5. </dependency>

JAVA.UTIL.LOGGING
如果您希望使用java.util.logging作为底层日志记录框架,那么您所需要做的就是声明“org. org”。将Slf4j: Slf4j -jdk14”作为pomc .xml文件中的依赖项,如下所示。除了slf4j-jdk14-${latest.stable.version}.jar,这将把slf4j-api-${latest.stable.version}.jar拉到你的项目中。请注意,显式地声明slf4j-api-${latest.stable.version}.jar上的依赖项是正确的,并且可能需要通过Maven的“最近定义”依赖项中介规则来强制执行该工件的正确版本。

  1. <dependency>
  2. <groupId>org.slf4j</groupId>
  3. <artifactId>slf4j-jdk14</artifactId>
  4. <version>${latest.stable.version}</version>
  5. </dependency>

Binary compatibility

SLF4J绑定指定一个工件,如SLF4J -jdk14.jar或SLF4J -log4j12.jar,用于将SLF4J绑定到底层日志框架,例如,java.util.logging和log4j。

混合SLF4J -api.jar和SLF4J绑定的不同版本可能会导致问题。例如,如果你正在使用slf4j-api-${latest.stable.version}.jar,那么你也应该使用slf4j-simple-${latest.stable.version}.jar,使用slf4j-simple-1.5.5.jar将不起作用。

然而,从客户端的角度来看,slf4j-api的所有版本都是兼容的。对于任何N和m,用slf4j-api-N.jar编译的客户端代码都可以用slf4j-api-M.jar完美地运行。您只需要确保绑定的版本与slf4j-api.jar的版本匹配即可。您不必担心项目中给定依赖项使用的slf4j-api.jar版本。您可以使用任何版本的slf4j-api.jar,只要slf4j-api.jar的版本和它的绑定匹配,就应该没问题。

在初始化时,如果SLF4J怀疑可能存在SLF4J -api与绑定版本不匹配的问题,它将发出关于怀疑不匹配的警告。

Consolidate logging via SLF4J

通常,给定的项目将依赖于依赖于SLF4J之外的日志api的各种组件。通常会找到依赖于JCL、java.utillog4j和SLF4J, 组合的项目。因此,需要通过单个通道合并日志记录。SLF4J通过为JCL、java.util.logging和log4j提供桥接模块来满足这个常见的用例。有关更多细节,请参阅“桥接遗留api”页面。

Mapped Diagnostic Context (MDC) support

“映射诊断上下文”本质上是由日志框架维护的映射,其中应用程序代码提供键值对,然后日志框架可以在日志消息中插入键值对。MDC数据在过滤消息或触发某些操作方面也非常有用。

SLF4J支持MDC或映射诊断上下文。如果底层日志记录框架提供了MDC功能,那么SLF4J将委托给底层框架的MDC。注意,此时只有log4j和logback提供MDC功能。如果底层框架不提供MDC,例如java.util。然后SLF4J将仍然存储MDC数据,但其中的信息需要由定制用户代码检索。

因此,作为SLF4J用户,您可以利用log4j或logback提供的MDC信息,但不必将这些日志记录框架作为依赖强加给用户。

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

Executive summary