一、日志门面
当我们的系统变的更加复杂的时候,我们的日志就容易发生混乱。随着系统开发的进行,可能会更新不同的日志框架,造成当前系统中存在不同的日志依赖,让我们难以统一的管理和控制。就算我们强制要求所有的模块使用相同的日志框架,系统中也难以避免使用其他类似 Spring,MyBatis 等其它的第三方框架,它们依赖于我们规定不同的日志框架,而且他们自身的日志系统就有着不一致性,依然会造成日志体系的混乱。
所以我们需要借鉴 JDBC 的思想,为日志系统也提供一套门面,那么我们就可以面向这些接口规范来开发,避免了直接依赖具体的日志框架。这样我们的系统在日志中,就存在了日志的门面和日志的实现。
常见的日志门面:
JCL,SLF4J
常见的日志实现:
JUL,log4j、logback,log4j2
日志框架出现的历史顺序:
log4j -> JUL -> JCL -> SLF4J -> logback -> log4j2
我们为什么要使用日志门面:
- 面向接口开发,不再依赖具体的实现类,减少代码的耦合。
- 项目通过导入不同的日志实现类,可以灵活的切换日志框架。
- 统一 API,方便开发者学习和使用。
- 统一配置便于项目日志的管理。
二、JCL
1、简介
JCL 全称为 Jakarta Commons Logging
,是 Apache 提供的一个通用日志 API。它是为“所有的 Java 日志实现”提供一个统一的接口,它自身也提供一个日志的实现,但是功能非常常弱(SimpleLog),所以一般一会单独使用它。他允许开发人员使用不同的具体日志实现工具:Log4j,JDK 自带的日志(JUL)。
JCL 有两个基本的抽象类:Log(基本记录器)和 LogFactory(负责创建 Log 实例)。
2、入门
1)建立 maven 工程
2)添加依赖
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
3)入门代码
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
public class Test {
public static void main(String[] args) {
// 创建日志对象
Log log= LogFactory.getLog(Test.class);
// 日志记录输出
log.fatal("fatal");
log.error("error");
log.warn("warn");
log.info("info");
log.debug("debug");
}
}
3、JCL 整合 log4j
直接加上 log4j 的依赖即可。
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
如果直接运行原来的代码会报错,如下:
log4j:WARN No appenders could be found for logger (top.zyxwmj.journal.demo.Test).
log4j:WARN Please initialize the log4j system properly.
log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.
原因:log4j 没有被初始化,初始化的方式有两种:
1)使用默认的配置
加入以下代码即可。
// 初始化配置信息
BasicConfigurator.configure();
2)使用配置文件
创建 log4j.properties
文件,并写入以下配置即可。
# 指定 RootLogger 顶级父元素默认配置信息
# 指定日志级别=trace,使用的 appender 为 console
log4j.rootLogger = ALL,console
# 指定控制台日志输出的 appender
log4j.appender.console = org.apache.log4j.ConsoleAppender
# 指定消息格式 layout
log4j.appender.console.layout = org.apache.log4j.SimpleLayout
JCL 已经淘汰就不再做过多的解释,主要讲解 SLF4J。
三、SLF4J
简单日志门面(Simple Logging Facade For Java)slf4j 主要是为了给 Java 日志访问提供一个标准、规范的 API 框架,其主要意义在于提供接口,具体的实现可以由其他日志框架,例如 log4j 和 logback 等。当前 slf4j 自己也提供了功能较为简单的实现,但是一般很少用到。对于一般的 Java 项目而言,日志框架会选择 slf4j-api
作为门面,配置上具体的实现框架(log4j、logback 等),中间使用桥接器完成桥接。slf4j 官网
1、slf4j 入门
1)建立 maven 工程
2)添加依赖
<!-- slf4j 日志门面 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>2.0.0-alpha1</version>
</dependency>
<!-- slf4j 简单的实现 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>2.0.0-alpha1</version>
<scope>test</scope>
</dependency>
3)快速入门
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Test {
public static final Logger LOGGER = LoggerFactory.getLogger(Test.class);
public static void main(String[] args) {
// 日志输出
LOGGER.error("error");
LOGGER.warn("warn");
LOGGER.info("info");
LOGGER.debug("debug");
// 参数注入
String name = "yi-Xing";
int age = 18;
LOGGER.info("用户:{},{}", name, age);
// 将系统的异常信息输出
try {
int i = 1 / 0;
} catch (Exception e) {
//e.printStackTrace();
LOGGER.error("出现异常", e);
}
}
}
2、slf4j 整合其他框架
slf4j 支持各种日志框架,slf4j 发行版附带了几个称为“slf4j 绑定”的 jar 文件称为 slf4j 的适配器,每个绑定对应一个受支持的框架。使用 slf4j 整合其他日志框架,首先导入 slf4j-api
依赖。
<!-- slf4j 日志门面 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>2.0.0-alpha1</version>
</dependency>
slf4j 本身是一个日志门面,内部是不提供具体实现的。如果只导入 slf4j-api
运行代码会报以下错误,因为没有对 slf4j 进行实现。
SLF4J: No SLF4J providers were found.
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#noProviders for further details.
整合 simple
上面快速入门的例子就是整合 simple,再导入 simple 的依赖即可使用。
<!-- slf4j 简单的实现 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>2.0.0-alpha1</version>
<scope>test</scope>
</dependency>
整合 logback
整合 logback,导入 logback-classic
和 logback-core
依赖即可。
<!-- slf4j 的具体实现 logback-->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.3.0-alpha5</version>
<exclusions>
<!--用来排除传递性依赖。-->
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</exclusion>
<exclusion>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--logback的基础模块-->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>1.3.0-alpha5</version>
</dependency>
整合 log4j
由于 log4j 是在 slf4j 之前开发的,并没有根据 slf4j 的规范,所以要想让 log4j 和 slf4j 进行整合需要导入 slf4j-log4j12
依赖。
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>2.0.0-alpha1</version>
</dependency>
如果提示如下信息,说明 log4j 没有初始化。
log4j:WARN No appenders could be found for logger (top.zyxwmj.journal.demo.Test).
log4j:WARN Please initialize the log4j system properly.
log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.
整合 log4j2
log4j2 有自己的日志门面,如果要和 slf4j 进行整合,需要添加log4j-slf4j-impl
依赖作为适配器。
<!-- slf4j 日志门面 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.30</version>
</dependency>
<!-- 基于 log4j2 的适配器 -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>2.13.1</version>
</dependency>
<!-- Log4j2 门面 API -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.13.1</version>
</dependency>
<!-- Log4j2 日志实现 -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.13.1</version>
</dependency>
整合 JUL
由于 JUL 是在 slf4j 之前开发的,也没有根据 slf4j 的规范,所以要想让 JUL 和 slf4j 进行整合需要导入 slf4j-jdk14
依赖。
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-jdk14</artifactId>
<version>2.0.0-alpha1</version>
</dependency>
slf4j 失效
如果不想打印日志,导入以下依赖即可,即可使 slf4j 失效
<!-- slf4j 的开关 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-nop</artifactId>
<version>2.0.0-alpha1</version>
<scope>test</scope>
</dependency>
3、slf4j 桥接旧的日志框架
通常,你依赖的某些组件依赖于 slf4j 以外的日志记录 API。为了解决这种情况,slf4j 附带了几个桥接模块,这些模块将对 log4j、JCL 和 Java.util.logging API 的调用重定向,就好像它们是 slf4j-api 一样。
桥接解决的是项目中日志遗留问题,当系统中存在之前的日志 API,可以通过桥接转换到 slf4j 的实现。
- 先去除之前老的日志框架的依赖。
- 添加 slf4j 提供的桥接组件。
- 为项目添加 slf4j 的具体实现。
桥接 log4j
假设我们项目现在使用的日志框架是 log4j。
1)依赖如下:
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
2)代码如下:
import org.apache.log4j.BasicConfigurator;
import org.apache.log4j.Logger;
public class Test {
public static final Logger LOGGER = Logger.getLogger(Test.class);
public static void main(String[] args) {
BasicConfigurator.configure();
// 日志输出
LOGGER.info("info");
}
}
3)进行升级:
现在我们项目升级,不想再使用 log4j 而想使用 slf4j 整合 logback,由于 log4j 和 slf4j 的语法不一样,如果直接更换需要修改源代码。如果我们不想修改源代码,可以使用 slf4j 的桥接器。我们只需要两步即可,首先删除 log4j
的依赖,然后添加以下依赖即可。
<!-- slf4j 日志门面 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>2.0.0-alpha1</version>
</dependency>
<!-- log4j 的桥接器 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>log4j-over-slf4j</artifactId>
<version>2.0.0-alpha1</version>
</dependency>
<!-- slf4j 的具体实现 logback-->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.3.0-alpha5</version>
</dependency>
桥接 JUL
桥接 JUL 和上面的方法一样,只需要把 log4j-over-slf4j 更换为 jul-to-slf4j 即可。
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jul-to-slf4j</artifactId>
<version>2.0.0-alpha1</version>
</dependency>
桥接 JCU
桥接 JCU 和上面的方法一样,只需要把 log4j-over-slf4j 更换为 jcl-over-slf4j 即可。
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>2.0.0-alpha1</version>
</dependency>
4、适配器和桥接器的应用场景
适配器是在项目之初,帮助 slf4j 和其他框架进行整合,使用 slf4j 语法进行编写日志,方便以后更换日志。而桥接器是对于老的项目,想对项目的日志框架进行升级,但又不想更改源代码的情况下使用。
注意问题:
- jcl-over-slf4j.jar 和 slf4j-jcl.jar 不能同时部署。前一个 jar 文件将导致 JCL 将日志系统的选择委托给 slf4j,后一个 jar 文件将导致 slf4j 将日志系统的选择委托给 JCL,从而导致死循环。
- log4j-over-slf4j.jar 和 slf4j-log4j12.jar 不能同时出现。
- jul-to-slf4j.jar 和 slf4j-jdk14.jar 不能同时出现
- 所有的桥接都只对 Logger 日志记录器对象有效,如果程序中调用了内部的配置类或者是 Appender、Filter 等对象,将无法产生效果。
5、slf4j 原理解析
- slf4j 通过 LoggerFactory 加载日志具体的实现对象。
- LoggerFactory 在初始化的过程中,会通过 performInitialization() 方法绑定具体的日志实现。
- 在绑定具体实现的时候,通过类加载器,加载 org/slf4j/impl/StaticLoggerBinder.class
- 所以,只要是一个日志实现框架,在 org.slf4j.impl 包中提供了一个自己的 StaticLoggerBinder 类,在其中提供具体日志实现的 LoggerFactory 就可以被 slf4j 所加载。
标题:日志门面——JCL、SLF4J
作者:Yi-Xing
地址:http://zyxwmj.top/articles/2020/03/22/1584871218647.html
博客中若有不恰当的地方,请您一定要告诉我。前路崎岖,望我们可以互相帮助,并肩前行!