SLF4J是目前最为流行的Java日志库。它的全名为Simple Logging Facade for Java。它仅是Java日志功能的外观层(Facade);它并没有实现任何记录日志的功能。因此,在实际项目中,开发人员还需要整合一个日志实现库(例如:log4j2或者Logback)。因为SLF4J提供了统一的接口,所以,开发人员不需要直接使用log4j2或者Logback。我们将在本章中着重介绍SLF4J的原理和使用方法。
SLF4J可以与log4j2、Logback、Java标准库JUL(java.util.logging)等许多流行的日志库结合在一起使用。SLF4J通过一个"绑定库"来绑定具体的日志库。如下图所示,如果SLF4J和Logback结合使用的话,在发布Java项目时还需要logback-classic.jar和logback-core.jar。
图一 SLF4J应用结构图
使用SLF4J的优势有:
如果项目使用SLF4J和Logback日志库并且使用Maven管理时,需要添加如下依赖关系。
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>2.0.0-alpha1</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.3.0-alpha5</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>1.3.0-alpha5</version>
</dependency>
如果项目使用SLF4J和Logback日志库并且使用Gradle管理时,需要添加如下依赖关系。
repositories {
mavenCentral()
}
dependencies {
implementation 'org.slf4j:slf4j-api:2.0.0-alpha1'
implementation 'ch.qos.logback:logback-classic:1.3.0-alpha5'
implementation 'ch.qos.logback:logback-core:1.3.0-alpha5'
}
如果当SLF4J没有绑定任何记录库时,SLF4J并不会输出任何日志记录。
SLF4J代码库包含五个重要的概念。
图二 日志消息处理流程图
SLF4J支持以下日志级别。
级别名称 | 级别描述 |
---|---|
FATAL | 用于记录特别严重的错误信息,这些错误可能导致应用程序停止服务。 |
ERROR | 用于记录运行时的错误。 |
WARN | 用于记录警告信息。这些信息用于提示开发人员,应用程序可能会出现错误。 |
INFO | 用于记录事件信息。这些信息用于记录应用程序发生的重要事件。 |
DEBUG | 用于记录调试信息。例如,记录应用程序的逻辑处理流程。 |
TRACE | 用于记录细节信息,较为不重要的信息。 |
SLF4J并不需要任何配置就可以使用。但是当SLF4J与Logback结合使用时,开发人员需要为Logback提供配置信息。
Logback支持通过配置文件设置配置信息和在程序中设置配置信息两种方式。如果开发人员使用配置文件的方式配置Logback,那么,开发人员需要准备一个XML格式的配置文件。当应用程序初始化时,Logback会按照如下顺序查找并读取这个配置文件。
值得注意的是,如果开发人员使用Maven管理项目,并且将logback-test.xml放在src/test/resources目录下,logback-test.xml文件可能不会包含在最终的包中。在这种情况下,开发人员需要使用另一个文件,例如:logback.xml,用于打包和产品发布。
Logback还提供了通过系统参数来设置配置文件的路径。如下面的例子所示,在运行Java程序时,开发人员可以使用logback.configurationFile来设置配置文件路径。
java -Dlogback.configurationFile=/path/to/logback.xml Example
Logback的配置文件logback.xml有着灵活的结构。下面的例子展示了一个基本的配置例子。在logback.xml文件中,其根节点为<configuration>。根结点可以包含0个或者多个<appender>节点,0个或者多个<logger>节点,和最多一个<root>节点。
<appender>节点用于配置日志消息处理类,其功能类似于Handler。<appender>节点包含0个或者1个<layout>节点,0个或者多个<encoder>,和0个或者多个<filter>。它们用于格式化和过滤日志消息。
<logger>节点可用于基于日志级别的过滤。<root>节点则设置logger的根结点(root logger)。根据Java工程中包的等级结构(Package Structure),开发人员可以为每个包设置logger。因此,从概念上看,"java"包的logger是"java.util"包的logger的父亲。"java.util"包的logger是"java.util.Vector"类的logger的父亲。因此,logger的根节点就是这个logger树的顶层根结点。每个logger可以设置一个级别。如果logger未设置级别的话,它将从它的父亲继承。以此类推,直到logger的根节点。
每条日志消息也会设置一个日志级别(例如:INFO、DEBUG等),只有当日志的级别不低于logger的级别时,日志消息才会被记录。换句话说,如果日志消息的级别低于logger的级别,该条日志消息会被丢弃。日志级别的高低如下所示。
TRACE < DEBUG < INFO < WARN < ERROR < FATAL
因此,当将logger的级别设置在INFO时,TRACE和DEBUG级别的日志消息会被丢弃。在日志文件中只会出现INFO、WARN和ERROR级别的消息。
如下是一个logback.xml的简单示例。该配置文件仅设置了logger根节点的级别为debug,并且使用<appender-ref>将其与<appender>关联起来。其意义是,但凡由logger根节点输出的日志消息都由这个<appender>格式化输出。
<configuration debug="true">
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<!-- encoders are by default assigned the type
ch.qos.logback.classic.encoder.PatternLayoutEncoder -->
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<root level="debug">
<appender-ref ref="STDOUT" />
</root>
</configuration>
小水滴提供了使用SLF4J+Logback日志功能的项目模板,方便感兴趣的读者下载试用:Maven,Gradle 。
SLF4J的使用方法非常简单。首先,使用LoggerFactory类的静态方法getLogger()创建Logger对象。getLogger()方法接收一个名称或者一个Class<?>类对象。然后,开发人员可以使用Logger类中的info()、warn()、trace()、error()和debug()方法记录相应级别的日志消息。
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Example {
private static Logger logger = LoggerFactory.getLogger(Example.class);
public static void main(String[] args) {
logger.info("This is an SLF4J example.");
}
}
如果结合Logback日志库,上述的程序记录的日志消息如下。这条消息包含了日志时间戳、日志级别、类名、所在的方法名和日志消息。
2020-10-13 19:22:23,639 INFO com.littlewaterdrop.Example [main] This is an SLF4J example.
开发人员常常使用当前类来创建Logger对象。如在上例中,在Example类中使用Example.class创建Logger对象。SLF4J提供了一个方便的功能,如果开发人员将系统属性slf4j.detectLoggerNameMismatch设置为true,那么,SLF4J会检查getLogger()方法的参数。如果参数的类与所在的类不同的话,SLF4J会报告一条警告消息"Detected logger name mismatch."。
package com.littlewaterdrop;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Example {
public static void main(String[] args) {
System.setProperty("slf4j.detectLoggerNameMismatch", "true");
Logger logger = LoggerFactory.getLogger(String.class);
logger.info("This is an SLF4J example.");
}
}
假设我们将String的类对象传入getLogger()方法后,上述程序会生成如下提示消息。
SLF4J: Detected logger name mismatch. Given name: "java.lang.String"; computed name: "com.littlewaterdrop.Example".
使用{}占位符能帮助开发人员方便的生成记录字符串。例如,开发人员可以在记录日志时传入参数,SLF4J会按照顺序将参数替代{}占位符。
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Example {
public static void main(String[] args) {
Logger logger = LoggerFactory.getLogger(Example.class);
logger.info("This is an SLF4J example in class {}.", "Example");
}
}
上述程序会输出如下类似的日志记录。
2020-10-13 19:22:23,639 INFO com.littlewaterdrop.Example [main] This is an SLF4J example in class Example.
Logger类的info()、debug()、error()、warn()、trace()方法接收多个参数,参数类型为Object。
void info(String msg);
void info(String format, Object arg);
void info(String format, Object... arguments);
void info(String format, Object arg1, Object arg2);
void debug(String msg);
void debug(String format, Object arg);
void debug(String format, Object... arguments);
void debug(String format, Object arg1, Object arg2);
void error(String msg);
void error(String format, Object arg);
void error(String format, Object... arguments);
void error(String format, Object arg1, Object arg2);
void warn(String msg);
void warn(String format, Object arg);
void warn(String format, Object... arguments);
void warn(String format, Object arg1, Object arg2);
void trace(String msg);
void trace(String format, Object arg);
void trace(String format, Object... arguments);
void trace(String format, Object arg1, Object arg2);
SLF4J还提供了方便的接口记录Throwable对象。这个方法可用于抓捕和记录异常对象的信息,非常有用。
void info(String msg, Throwable t);
void debug(String msg, Throwable t);
void error(String msg, Throwable t);
void warn(String msg, Throwable t);
void trace(String msg, Throwable t);
例如:在下面的例子中,开发人员直接将Exception对象传入logger.error()方法。
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Example {
public static void main(String[] args) {
Logger logger = LoggerFactory.getLogger(AnotherClass.class);
try {
Thread.sleep(1000);
} catch (IllegalArgumentException | InterruptedException ex) {
logger.error("Sleep interruped.", ex);
}
}
}
本章介绍了SLF4J的基本原理和使用方法。因为SLF4J是日志外观层,独立于日志记录库。因此,开发人员在开发时,无需在源代码中明确指出使用哪个日志库。使用的日志库在程序发布时才需确定下来。
注册用户登陆后可留言