文章目录
- 背景
- SLF4J概述
- 切换日志框架
- 实际应用
- 配合自身简单日志实现(slf4j-simple)
- 配置logback日志实现
- 配置Log4J日志实现(需适配器)
- 配置JUL日志实现(需适配器)
- 添加slf4j-nop依赖(日志开关)
- 桥接旧的日志实现框架
背景
随着系统开发的进行,可能会更新不同的日志框架,造成当前系统中存在不同的日志依赖,让我们难以统一的管理和控制。就算我们强制要求所有的模块使用相同的日志框架,系统中也难以避免使用其他类似spring,mybatis等其他的第三方框架,它们依赖于我们规定不同的日志框架,而且他们自身的日志系统就有着不一致性,依然会出来日志体系的混乱。
SLF4J概述
简单日志门面(Simple Logging Facade For Java) SLF4J主要是为了给Java日志访问提供一套标准、规范的API框架,其主要意义在于提供接口,具体的实现可以交由其他日志框架,例如log4j和logback等。当然slf4j自己也提供了功能较为简单的实现,但是一般很少用到。对于一般的Java项目而言,日志框架会选择slf4j-api作为门面,配上具体的实现框架(log4j、logback等),中间使用桥接器完成桥接。
切换日志框架
application下面的SLF4J API表示slf4j的日志门面,包含三种情况:
- 若是只导入slf4j日志门面没有导入对应的日志实现框架,那么日志功能将会是默认关闭的,不会进行日志输出的。
- 蓝色图里Logback、slf4j-simple、slf4j-nop出来的比较晚就遵循了slf4j的API规范,也就是说只要导入对应的实现就默认实现了对应的接口,来实现开发。
- 对于中间两个日志实现框架log4j(slf4j-log4j12)、JUL(slf4j-jdk14)由于出现的比slf4j早,所以就没有遵循slf4j的接口规范,所以无法进行直接绑定,中间需要加一个适配层(Adaptationlayer),通过对应的适配器来适配具体的日志实现框架,其对应的适配器其实就间接的实现了slf4j-api的接口规范。
注意:在图中对于logback需要引入两个jar包,不过在maven中有一个传递的思想,当配置logback-classic时就会默认传递core信息,所以我们只需要引入logback-classic的jar包即可。
相关注意点
在使用slf4j日志门面的过程中,若是引入了两个日志实现框架会报以下错误,并会默认实现第一个引入的日志实现:
这里是同时配置simple
以及logback
情况,以pom.xml
中配置顺序有关
实际应用
配合自身简单日志实现(slf4j-simple)
若想使用自身的日志实现框架,需要引入第三方jar
包slf4j-simple
(slf4j自带实现类):
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>1.7.5</version>
</dependency>
其中该坐标包含了对应的slf4j-api
的依赖,可以不用手动导入slf4j-api
。
测试程序
public class LogTest {
//获取Logger实例
public static final Logger LOGGER = LoggerFactory.getLogger(LogTest.class);
public static void main(String[] args) {
System.out.println(LOGGER.getName());
//打印日志记录
LOGGER.error("error");
LOGGER.warn("warn");
LOGGER.info("info");
LOGGER.debug("debug");
LOGGER.trace("trace");
//占位符输出
String name = "changlu";
int age = 20;
LOGGER.info("报错,name:{},age:{}",name,age);
//打印堆栈信息
try {
int i = 5/0;
}catch (Exception e){
LOGGER.error("报错",e);
}
}
}
默认日志等级为INFO
,能够实现占位符输出,并且可以在日志等级方法中传入异常实例,来打印对应的日志信息。
注意点:若是我们只使用日志门面而没有导入指定的日志实现框架,调用Logger
实例并调用日志方法会出现以下错误:
配置logback日志实现
引入logback-classic
的jar包,其中包含有slf4j-api
以及logback-core
的依赖,所以只需要引入该依赖即可:
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
public class LogTest {
//获取Logger实例
public static final Logger LOGGER = LoggerFactory.getLogger(LogTest.class);
public static void main(String[] args) {
System.out.println(LOGGER.getName());
//打印日志记录
LOGGER.error("error");
LOGGER.warn("warn");
LOGGER.info("info");
LOGGER.debug("debug");
LOGGER.trace("trace");
//占位符输出
String name = "changlu";
int age = 20;
LOGGER.info("报错,name:{},age:{}",name,age);
//打印堆栈信息
try {
int i = 5/0;
}catch (Exception e){
LOGGER.error("报错",e);
}
}
}
配置Log4J日志实现(需适配器)
首先添加日志框架实现依赖
之前在1.3中介绍,对于Log4j
、JUL
这些比较早出现的日志实现框架需要有对应的适配层,在这里我们引入对应的适配器slf4j-log412
的依赖坐标:
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.25</version>
</dependency>
slf4j-log4j12
坐标中实际就包含了Log4j
以及slf4j-api
依赖,所以我们添加该坐标即可。
添加log4j.properties
配置文件
# rootLogger日志等级为trace,输出到屏幕上
log4j.rootLogger = trace,console
# console
log4j.appender.console = org.apache.log4j.ConsoleAppender
log4j.appender.console.layout = org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern= [%-5p]%r %l %d{yyyy-MM-dd HH:mm:ss:SSS} %m%n
测试程序:
public class LogTest {
//获取Logger实例
public static final Logger LOGGER = LoggerFactory.getLogger(LogTest.class);
public static void main(String[] args) {
System.out.println(LOGGER.getName());
//打印日志记录
LOGGER.error("error");
LOGGER.warn("warn");
LOGGER.info("info");
LOGGER.debug("debug");
LOGGER.trace("trace");
//占位符输出
String name = "changlu";
int age = 20;
LOGGER.info("报错,name:{},age:{}",name,age);
//打印堆栈信息
try {
int i = 5/0;
}catch (Exception e){
LOGGER.error("报错",e);
}
}
}
配置JUL日志实现(需适配器)
对于slf4j
日志门面实现JUL
日志框架需要使用是适配器来实现slf4
j的日志接口,我们直接添加对应适配器依赖如下:
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-jdk14</artifactId>
<version>1.5.6</version>
</dependency>
JUL
是我们jdk
自带的日志框架,所以不需要额外引入jar包,引入slf4j-jdk14
坐标,其中就包含了slf4j-api
的依赖,所以我们只需要引入一个坐标即可。
测试程序
public class LogTest {
//获取Logger实例
public static final Logger LOGGER = LoggerFactory.getLogger(LogTest.class);
public static void main(String[] args) {
System.out.println(LOGGER.getName());
//打印日志记录
LOGGER.error("error");
LOGGER.warn("warn");
LOGGER.info("info");
LOGGER.debug("debug");
LOGGER.trace("trace");
//占位符输出
String name = "changlu";
int age = 20;
LOGGER.info("报错,name:{},age:{}",name,age);
//打印堆栈信息
try {
int i = 5/0;
}catch (Exception e){
LOGGER.error("报错",e);
}
}
}
添加slf4j-nop依赖(日志开关)
当添加了slf4j-nop
坐标后,其相当于一个日志开关,导入实现以后就不会使用任何实现框架:
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-nop</artifactId>
<version>1.7.27</version>
</dependency>
测试程序:
public class LogTest {
//获取Logger实例
public static final Logger LOGGER = LoggerFactory.getLogger(LogTest.class);
public static void main(String[] args) {
LOGGER.error("error");
LOGGER.warn("warn");
LOGGER.info("info");
LOGGER.debug("debug");
LOGGER.trace("trace");
}
}
默认就关闭了slf4j
的日志框架使用。
桥接旧的日志实现框架
直接举场景来说明:对于一些老项目直接使用的是Log4j或JUL的日志实现框架,并没有使用到日志门面来进行管理日志框架,当项目需要迭代升级时,我们想把原先的日志实现框架切换为logback,此时会出现一个问题,若是我们直接将对应的日志jar包更改为logback,那么项目中会出现大量报错,因为原先引入的包是import org.apache.log4j.Logger;,此时就会出现问题,我们需要重新修改大量的代码,需要耗费大量的时间与精力。
解决方案:在slf4j中可以使用桥接器从而让我们不用修改一行代码实现日志框架的切换。在slf4j中附带了几个桥接木块,这些模块对于log4j、JCL和JUL的API调用重定向(其实就是全限定名与原来的完全相同)。
下图包含了对应的解决方案:
可用log4j-over-slf4j.jar
替代Log4j
log4j-over-slf4j桥接器使用
模拟场景:
老项目直接使用的是org.apache.log4j.Logger
,现今项目迭代升级,需要使用Logback
日志框架
我们首先将log4j
jar包移除,之后引入logback-classic
依赖坐标,此时就会报错
解决方案:使用桥接器log4j-over-slf4j
引入坐标依赖:
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>log4j-over-slf4j</artifactId>
<version>1.7.27</version>
</dependency>
不用修改任何代码即可替换日志框架