SLF4J 用户使用手册

SLF4J是Simple Logging Facade For Java的缩写,从名字中的Facade可以看出它是一个门面。SLF4J并不是一个具体实现,而是一个日志框架,或者说日志框架的抽象,可以在程序运行时,更新Classpath中的jar包加载具体的日志实现,比如说log4j、logback。这个具体的实现可以称之为binding
SINCE 1.6.0 如果没有在classpath中发现binding,那么SLF4J不会做任何操作
SINCE 1.7.0 接口Logger支持接受多种参数的方法,以替代Object[],它需要只是JDK1.5以上的版本。多种参数变量将在Java编译器后台被装换成object[],因此编译器生成的接口在1.7x 和1.6.x中是无法区分的,因此1.7.x是完全100%兼容1.6.x的。
SINCE 1.7.5 日志索引时间得到了显著的提升,建议用户使用SLF4J 至少1.7.5以后的版本。
SINCE 1.7.9 通过将slf4j.detectLoggerNameMismatch系统属性设置为true,SLF4J可以自动发现名称错误的记录器。
SINCE 2.0.0 SLF4J 2.0.0 API依赖了JDK8,并且提供了向后兼容的流式API。因为向后兼容性,所以现有的日志框架不需要做任何修改就可以实现使用流式API了。

Hello World

按照编程传统,下面是一个使用SLF4J输出“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");
  }
}

编译、运行HelloWorld将在控制台输出以下内容。

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

上面的warning,说明了没有在classpath中找到slf4j binding,将slf4j-simple-1.7.28.jar加入classpath中即可输出如下的正确结果

0 [main] INFO HelloWorld - Hello World

典型的使用模式

下面的代码展示了SLF4J典型的使用模式。注意第15行占位符{}的使用方式。请尽量使用占位符的方式来代替字符串拼接,这样可以避免多余的计算,显著提高框架的性能。

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

流式API

这个想法是使用LoggingEventBuilder逐段构建日志事件,并在事件完全构建后进行日志记录。atTrace(),atInfo(),atWarn()atError()方法都是org.slf4j.Logger接口中的新方法,它将返回一个LogginEventBuilder,在disable级别的日志level,这个方法不会做任何的事情,这是一个纳秒级别的优化。
下面是一些用法示例:

logger.atInfo().log("Hello world");
// 等同于
logger.info("Hello world.");

下面的语句在功能上等同与以前接口

int newT = 15;
int oldT = 16;
// using traditional API
logger.debug("Temperature set to {}. Old temperature was {}.", newT, oldT);
// using fluent API, add arguments one by one and then log message
logger.atDebug().addArgument(newT).addArgument(oldT).log("Temperature set to {}. Old temperature was {}.");
// using fluent API, log message with arguments
logger.atDebug().log("Temperature set to {}. Old temperature was {}.", newT, oldT);
// using fluent API, add one argument and then log message providing one more argument
logger.atDebug().addArgument(newT).log("Temperature set to {}. Old temperature was {}.", oldT);
// using fluent API, add one argument with a Supplier and then log message with one more argument.
// Assume the method t16() returns 16.
logger.atDebug().addArgument(() -> t16()).log(msg, "Temperature set to {}. Old temperature was {}.", oldT);

在部署的时候绑定日志实现框架

前面提到了SLF4J支持多种日志框架,在SLF4J发布的时候,会一起发布多种SLF4J binding jar,每一种binding都可以实现SLF4J到一个具体日志实现的绑定。

  • slf4j-log4j12-1.7.28.jar:绑定了log4j 1.2
  • slf4j-jdk14-1.7.28.jar: 绑定了JDK1.4的java.util.logging
  • slf4j-nop-1.7.28.jar: 绑定了NOP
  • slf4j-simple-1.7.28.jar: 绑定了一个Simple实现,它将所有事件输出到System.err,只有INFO以及其INFO级别以上的信息将被打印。
  • slf4j-jcl-1.7.28.jar:绑定了 Jakarta Common Logging
  • logback-classic-1.2.3.jar: 需要logback-core-1.2.3.jar,Logback直接的的实现了SLF4j的接口org.slf4j.Logger,因此,结合使用SLF4J和logback会涉及严格的零内存和计算开销。
    如果要切换日志实现框架,只需要替换classpath中的slf4j binding即可。例如:将java.util.logging 换为log4j,需要将slf4j-jdk14-1.7.28.jar替换为slf4j-log4j12-1.7.28.jar。
    SLF4J在编译期间执行了硬链接,运行期间只有一种日志实现框架能够被使用,所以不要在classpath中放入多种binding SLF4J接口和各种adapter代码实现都非常简单,大多数熟悉Java代码的开发人员都能够在1小时能阅读并理解源码。同时,阅读SLF4J源码也不需要了解类加载器,因为SLF4J没有直接使用类加载器,所以SLF4J不会遇到累加器的问题,也不会遇到Jakarta Commons Logging中内存泄露问题。
    根据上面的SLF4J接口和模型,开发人员应该很实现一个SLF4J binding。

类库

类库和组件的作者应该针对SLF4J接口进行编码,这样终端用户就可以通过classpath中的jar包,选择自己的需要选择日志框架。这是一种简单并且行之有效的办法。
基本规则:内嵌式的组件、类库应该避免声明依赖任何SLF4J binding,仅仅依赖slf4j-api就够了。

为Logging声明项目依赖

LOGBACK-CLASSES 如果你使用loback-classic作为底层日志框架,你只需要像下面示例代码一样,在pom.xml中声明就可以了,它会将logback-core-1.2.3.jar以及slf4j-api-1.7.28.jar都添加到项目中。

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

LOG4J 如果你想要使用log4j作为底层日志框架实现,那么你需要在pom.xml中声明"org.slf4j:slf4j-log4j12-1.7.28.jar"就可以了。他将引入slf4j-log4j12-1.7.28.jar, slf4j-api-1.7.28.jar log4j-1.2.17.jar 到你的项目中。

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

兼容性

从客户的角度来讲,所有版本的slf4j-api都是兼容的。slf4j-api-N能够和slf4j-api-M完全的兼容,因此不需要担心有太多slf4j-api的依赖。 但是一定需要关注和保证binding。 binding需要和slf4j-api匹配。

通过slf4j合并日志

有时候,你的工程依赖了各种不同的组件,而这些组件并没有使用slf4j,可能是common log、log4j等。slf4j为提供了bridging解决这种场景

posted on 2020-02-23 12:01  yipianlarou  阅读(706)  评论(0编辑  收藏  举报