说到日志工具,日常工作或学习中肯定听过这些名词log4j、logback、jdklogging、slf4j、commons-logging等,它们之间有什么关系,在整个日志体系中又扮演什么角色呢?日志框架分为三大部分,包括日志门面、日志适配器、日志库。利用门面设计模式,即Facade来进行解耦,使日志使用变得更加简单,如下图所示:
1.日志门面
门面设计模式是面向对象设计模式中的一种,日志框架采用的就是这种模式,类似JDBC的设计理念。它只提供一套接口规范,自身不负责日志功能的实现,目的是让使用者不需要关注底层具体是哪个日志库来负责日志打印及具体的使用细节等。目前用得最为广泛的日志门面有两种: slf4j 和commons-logging
2.日志库
它具体实现了日志的相关功能,主流的日志库有三个,分别是log4j、log-jdk、logback。最早Java要想记录日志只能通过System.out或System.err来完成,非常不方便。log4j就是为了解决这一问题而提出的,它是最早诞生的日志库。接着JDK也在1.4版本引入了一个日志库javai.logging.Logger,简称logjdk.这样市面上就出现两种日志功能的实现,开发者在使用时需要关注所使用的日志库的具体细节。logback是最晚出现的,它与1og4j出自同一个作者,是log4j的升级版且本身就实现了slf4j 的接口。
3.日志适配器
日志适配器分两种场景:
(1)日志门面适配器,因为slf4j规范是后来提出的,在此之前的日志库是没有实现sIf4j的接口的,例如log4j;所以,在工程里要想使用slf4j+log4j的模式,就额外需要一个 适配器( slf4j-log4j12 )来解决接口不兼容的问题。
(2)日志库适配器,在一些老的工程里,开始为了开发简单而直接使用了日志库API来完成日志打印,随着时间的推移相将原来直接调用日志库的模式改为业界标准的门面模式(例如slf4j+logback组合),但老工程代码里打那日志的地方大多,难以改动,所以需要一 个适配器来完成从旧日志库的API到slf4的路由,这样在不改动原有代码的情况下也能使用slf4j来统管理日志, 而且后续自由替换具体日志库也不成问题。
我们了解了日志家族里的成员及其作用,接下来将以Maven工程为例介绍如何在工程里进行日志集成。
如果是新工程,则推荐使用slf4j+logback模式。因为logback自身实现了slf4j 的接口,无须额外引入适配器,另外logback是log4j的升级版,具备比log4j更多的优点,可通过如下配置进行集成:
<!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-api -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.8.0-beta2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/ch.qos.logback/logback-classic -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/ch.qos.logback/logback-core -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>1.2.3</version>
</dependency>
如果是老工程使用的都是log4,则需要根据所用的日志库来确定门面适配器,通常情况下老工程使用的是log4j,因此以logj日志库为例,可通过如下配置进行集成:
<dependency>
<groupid>org.slf4j</groupid>
<artifactid>slf4j-api</artifactid>
<version>${slf4j-api .version}</version>
</dependency>
<dependency>
<groupid>org.slf4j</groupid>
<artifactid>slf4j- log4j12</artifactid>
<version>${slf4j-1og4j12. version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/log4j/log4j -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
至此我们的工程就完成了日志框架的集成,再加上一个日志配置文件(如logback.xml、log4j.xml等), 并在工程启动时加载,然后就可以进行日志打印了,示例代码如下:
private static final Logger logger = LoggerFactory . getLogger (Abc.class);
注意,logger 被定义为static 变量,是因为这个logger与当前类绑定,避免每次都new一个新对象,造成资源浪费,甚至引发OutOfMemoryError问题。
另外,在使用slf4j+日志库模式时,要防止日志库冲突,一旦发生则可能会出现日志打印功能失效的问题。例如,某个业务的网站页面出现了500错误,但开发工程师翻遍整个系统的日志文件都没有发现任何异常日志。线下模拟调试发现:错误发生时有异常对象抛出并被框架捕获,然后执行了日志打印的相关代码,但实际没有输出到日志文件。经开发工程师深入排查,当前工程代码中配置的日志库为log4j,但工程依赖的一一个 jar 包间接地引入了logback 日志库,导致打印日志的Logger引用实际指向c.qos.logback cssis.Loggers对象,二者的冲突引发了日志打印失效的问题。