(日志框架之日志门面SLF4J的使用)
SLF4J概述
SLF4J (Simple Logging Facade for Java) 是一种 Java 编程语言的日志门面(logging facade)。它提供了一种将应用程序代码与特定日志记录(logging)实现(如 Log4j,java.util.logging 和 Apache commons-logging 等)分离的方式。
简单日志门面SLF4J主要是为了给Java日志访问提供一套标准、规范的API框架,其主要意义在于提供接口,具体的实现交由其他日志框架,例如log4j和logback等。
slf4j自己也提供了功能较为简单的实现,但是一般很少用到。
对于一般的Java项目而言,日志框架会选择slf4j-api作为门面,配上具体的实现框架(log4j、logback等),中间使用桥接器完成桥接。
SLF4J是目前市面上最流行的日志门面。现在的项目中,基本上都是使用SLF4J作为日志系统。
SLF4J日志门面主要提供两大功能:
日志框架的绑定: 将应用程序代码与具体的日志记录实现绑定,还可以在运行时切换不同的日志记录实现。
日志框架的桥接:将日志记录到一个共同的目标日志API中,可以维护更好的可读性和可维护性的应用程序
SLF4J的简单使用
<!-- slf4j 日志门面 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.26</version>
</dependency>
<!-- slf4j 内置的简单实现 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>1.7.21</version>
</dependency>
public class Slf4j {
public static final Logger LOGGER = LoggerFactory.getLogger(Slf4j.class);
@Test
public void test() {
//日志输出
LOGGER.error("error");
LOGGER.warn("wring");
LOGGER.info("info");
LOGGER.debug("debug");
LOGGER.trace("trace");
//使用占位符输出日志信息
String name = "hello slf4j";
LOGGER.info("占位符输出日志信息:{}", name);
//捕获异常,让日志输出,而非输出堆栈信息
try {
int i = 1 / 0;
} catch (Exception e) {
// e.printStackTrace();
LOGGER.error("异常输出:", e);
}
}
}
14:25:02.512 [main] ERROR cn.ybzy.Slf4j - error
14:25:02.520 [main] WARN cn.ybzy.Slf4j - wring
14:25:02.520 [main] INFO cn.ybzy.Slf4j - info
14:25:02.520 [main] INFO cn.ybzy.Slf4j - 占位符输出日志信息:hello slf4j
14:25:02.525 [main] ERROR cn.ybzy.Slf4j - 异常输出:
java.lang.ArithmeticException: / by zero
at cn.ybzy.Slf4j.test(Slf4j.java:27)
SLF4J与日志框架的绑定
slf4j日志门面与日志实现框架的绑定关系如下: 日志框架出现的时间顺序:
log4j -->JUL-->JCL--> slf4j --> logback --> log4j2
因此,在实现日志框架的绑定/切换的时候,log4j与jul日志框架需要间接添加日志的适配器
日志框架的绑定过程
1.导入SLF4J库
需要将 SLF4J 库添加到应用程序的类路径中,以便应用程序可以使用 SLF4J 的 API。如添加slf4j-api的依赖
2.导入绑定具体的日志实现框架
需要导入支持 SLF4J 的日志框架库,如 Log4j、java.util.logging 或 Apache commons-logging
注意:
1.绑定已经实现了slf4j的日志框架,直接添加对应依赖
2.绑定没有实现slf4j的日志框架,先添加日志的适配器,再添加实现类的依赖
3.slf4j有且仅有一个日志实现框架的绑定(如果出现多个默认使用第一个依赖日志实现)
3.配置日志框架
需要配置日志框架,以便它可以与 SLF4J 协同工作。这通常涉及到在应用程序中进行配置文件的创建并在日志框架中启用 SLF4J
3.使用 SLF4J API 记录日志
在应用程序中,使用SLF4J的API在项目中进行统一的日志记录,达到了应用程序与绑定的日志框架解耦。
使用 SLF4J 中的 Logger 接口记录日志:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class MyClass {
private static final Logger logger = LoggerFactory.getLogger(MyClass.class);
public void myMethod(){
logger.info("info");
}
}
slf4j+slf4j-simple
<!-- slf4j 日志门面 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.26</version>
</dependency>
<!-- slf4j 内置的简单实现 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>1.7.21</version>
</dependency>
配置示例
slf4j-simple提供了一个默认的日志配置,将不同日志级别的日志信息打印到控制台上。也可在项目类路径下,创建名为
simplelogger.properties
的配置文件自定义一些配置
# 默认日志等级,当在源代码中未指定时使用
org.slf4j.simpleLogger.defaultLogLevel=debug
# 日志文件名,如果日志输出到文件中
# org.slf4j.simpleLogger.logFile=mylogfile.log
# 只显示Logger名称的最后一部分(最末尾的点之后的部分)
org.slf4j.simpleLogger.showShortLogName=true
# 显示每个日志信息的时间和日期
org.slf4j.simpleLogger.showDateTime=true
# 日志消息的日期和时间格式
org.slf4j.simpleLogger.dateTimeFormat=yyyy-MM-dd HH:mm:ss.SSS
# 特定记录器的日志等级
# org.slf4j.simpleLogger.log.com.foo=warn
# 替换 WARN 级别输出的字符串
# org.slf4j.simpleLogger.warnLevelString=WARNING
slf4j+logback
<!-- slf4j 日志门面 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.26</version>
</dependency>
<!--logback 日志实现-->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
配置示例
创建 Logback 配置文件 logback.xml,将其添加到 classpath 下
<configuration>
<!-- 控制台输出的 appender -->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} -
%msg%n
</pattern>
</encoder>
</appender>
<!-- 日志文件输出的 appender -->
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>logs/myLogFile.log</file>
<append>false</append>
<encoder>
<pattern>%d{HH:mm:ss} [%thread] %-5level %logger{36} -
%msg%n
</pattern>
</encoder>
</appender>
<!-- 各个包的日志级别 -->
<logger name="com.example.myapp" level="INFO" />
<logger name="org.springframework.data" level="DEBUG" />
<logger name="org.hibernate" level="WARN" />
<!-- 日志级别 root 日志级别 -->
<root level="INFO">
<appender-ref ref="CONSOLE" />
<appender-ref ref="FILE" />
</root>
</configuration>
slf4j+nop
引入slf4j-api且不引入具体实现或者引入slf4j-api+slf4j-nop将会关闭日志输出。
SLF4J NOP是一种特殊类型的SLF4J绑定,它不会记录日志。但是,在程序代码中还是可以使用完整的SLF4J API,因为SLF4J NOP绑定实现所有必需的SLF4J API方法,但它不执行任何实际的记录行为。
<!-- slf4j 日志门面 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.26</version>
</dependency>
<!-- nop日志开关-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-nop</artifactId>
<version>1.7.25</version>
</dependency>
配置示例
不需要在代码中进行任何配置或初始化,因为SLF4J NOP是一个绑定,它允许您在您的代码中使用SLF4J API而不执行实际的日志记录。
slf4j+log4j
<!-- slf4j日志门面 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.26</version>
</dependency>
<!-- 绑定log4j日志实现,需要导入slf4j-log4j12适配器-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.12</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
配置示例
在src/main/resources文件夹下,创建一个名为log4j.properties的文件。在该文件中,可以指定要记录的级别、日志文件的输出位置、日志格式等。
#配置根日志级别
log4j.rootLogger=INFO, stdout, FILE
#配置控制台输出
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
#配置日志文件输出
log4j.appender.FILE=org.apache.log4j.RollingFileAppender
log4j.appender.FILE.File=./logs/application.log
log4j.appender.FILE.MaxFileSize=10MB
log4j.appender.FILE.MaxBackupIndex=10
log4j.appender.FILE.layout=org.apache.log4j.PatternLayout
log4j.appender.FILE.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
slf4j+jul
<!-- slf4j日志门面 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.26</version>
</dependency>
<!-- 绑定jul日志实现,需要导入slf4j-jdk14适配器-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-jdk14</artifactId>
<version>1.7.25</version>
</dependency>
配置示例
在src/main/resources文件夹下创建一个名为logging.properties的文
# 将JUL的日志委托给SLF4J API
handlers = org.slf4j.bridge.SLF4JBridgeHandler
# 配置的是日志级别
.level = FINEST
org.slf4j.bridge.SLF4JBridgeHandler.level = FINEST
SLF4J桥接旧的日志框架
SLF4J桥接旧的日志框架,就是使用SLF4J的桥接器,替换原有的日志框架。
SLF4J桥接的主要目的是将日志记录到一个共同的目标日志API中,可以维护更好的可读性和可维护性的应用程序。
SLF4J桥接旧的日志框架示意图:
SLF4J提供的桥接器
<!-- log4j-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>log4j-over-slf4j</artifactId>
<version>${version}</version>
</dependency>
<!-- jul -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jul-to-slf4j</artifactId>
<version>${version}</version>
</dependency>
<!--jcl -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>${version}</version>
</dependency>
SLF4J桥接旧的日志框架流程:
1. 移除老的日志框架的依赖
2. 添加SLF4J提供的桥接组件
3. 添加SLF4J的具体实现
桥接旧的日志框架,如:将系统使用的logf4j日志框架切换到logback日志框架
旧的日志系统
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
public class Log4j {
public static final Logger LOGGER = Logger.getLogger(Log4j.class);
@Test
public void test()throws Exception{
LOGGER.info("hello lgo4j");
}
}
INFO cn.ybzy.Log4j.test(Log4j.java:12) 2021-03-21 15:11:30,580 hello lgo4j
移除旧日志框架的依赖
当移除log4j的依赖时,项目中使用到的地方将报错
添加SLF4J提供的桥接组件
引入slf4j日志门面与log4j的桥接器,原来报错的地方全部恢复正常
<!-- slf4j日志门面 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.26</version>
</dependency>
<!--配置log4j的桥接器-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>log4j-over-slf4j</artifactId>
<version>1.7.25</version>
</dependency>
添加SLF4J的具体实现
<!--logback 日志实现-->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
桥接旧的日志框架后测试:发现日志输出发生变法,桥接成功。
15:18:48.591 [main] INFO cn.ybzy.Log4j - hello lgo4j