从原理到实践彻底搞懂 Java 日志系统

引言

你是否还在用system.out.print(“”)来追踪程序的重要运行信息?

你是否因无法区分commons-logging.jar、log4j.jar、slf4j-api.jar等日志框架而烦恼?

你是否因为日志框架不统一而纠结是否改代码而惆怅?

没关系,本文带你走进Java日志体系,从原理到实践解决你的困惑。

一、日志框架分类

1、门面型日志框架:不实现日志功能,仅整合日志

1)JCL:一套Apache基金所述的java日志接口,由Jakarta Commons Logging,更名为Commons Logging;

2)SIF4J:一套简易的Java日志门面,全称为Simple Logging Facade for Java。

2、记录性日志框架:实现日志的功能

1)JUL:JDK中的日志记录工具,自Java1.4来由官方日志实现;

2)Log4j:具体的日志实现框架;

3)Log4j2:具体日志实现框架;

4)Logback:一个具体的日志实现框架。

二、日志框架的发展演变

1、Log4j

在JDK1.3版本及以前,Java日志的实现依赖于System.out.print()、System.err.println()或者e.printStackTrace()、Debug日志被写到STDOUT流,错误日志被写到STDERR流。这样的日志系统无法定制且粒度太粗,无法精确定位错误。

Gülcü于2001年发布了Log4j框架,也就是后来Apache基金会的顶级项目。Log4j定义的Logger、Appender、Level等概念如今已经被广泛使用。Log4j 的短板在于性能,在Logback和 Log4j2出来之后,Log4j的使用也减少了,目前已停止更新。

2、JUL

受Logj启发,Sun在Java1.4版本中引入了java.util.logging,但是jull功能远不如log4j完善,开发者需要自己编写Appenders(Sun称之为Handlers),且只有两个Handlers可用(Console和File),jul在Java1.5以后性能和可用性才有所提升。

3、JCL

JCL(commons-logging)是一个门面框架,它由于项目的日志打印必然选择两个框架中至少一个,利用JCL只提供 Log API,不提供实现,实现采用Log4j或者 JUL 。

4、SLF4j

SLF4J(Simple Logging Facade for Java)和 Logback 也是Gülcü创立的项目,目的是为了提供更高性能的实现。

从设计模式的角度说,SLF4J是用来在log和代码层之间起到门面作用,类似于 JCL的Log Facade。对于用户来说只要使用SLF4J提供的接口,即可隐藏日志的具体实现,SLF4J提供的核心API是一些接口和一个LoggerFactory的工厂类,用户只需按照它提供的统一纪录日志接口,最终日志的格式、纪录级别、输出方式等可通过具体日志系统的配置来实现,因此可以灵活的切换日志系统。

三、日志实现框架实践

1、采用Log4j日志框架实现

1)Log4j为开源组件,使用前需要添加依赖:

log4j

log4j

2)同时需要添加log4j.properties配置文件;

配置文件内容:

log4j.rootLogger=trace, stdout

log4j.appender.stdout=org.apache.log4j.ConsoleAppender

log4j.appender.stdout.layout=org.apache.log4j.PatternLayout

log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m%n

3)测试代码:

importorg.apache.log4j.Logger;

publicclassLog4jTest{

publicstaticvoidmain(String[]args){

Logger  logger=Logger.getLogger(Log4jTest.class);

logger.info(logger.getClass().getName());

logger.info("apache日志框架log4j");

}

}

4)运行结果:

2、使用JUL日志框架实现

1)JUL日志系统在JDK中已引入,故无需导包,代码如下:

importjava.util.logging.Logger;

publicclassJulTest{

publicstaticvoidmain(String[]args){

Logger logger=Logger.getLogger(JulTest.class.getName());

//判断日志使用类型

logger.info(logger.getClass().getName());

logger.info("官方JDK日志框架Jul");

}

}

2)运行结果:

四、日志门面框架整合日志实现框架

使用日志门面框架缘由:在阿里开发手册上有关于日志门面使用系统的强制规约:

应用中不可直接使用日志系统(log4j、logback)中的 API ,而应依赖使用日志框架中的 API 。使用门面模式的日志框架,有利于维护和各个类的日志处理方式的统一。 

1、Sif4j门面框架+Log4j实现

1)添加slf4j的核心依赖:

org.slf4j

slf4j-api

2)Sif4j门面框架+Log4j实现使用的桥接器:

桥接器:日志门面接口本身通常并没有实际的日志输出能力,它底层还是需要去调用具体的日志框架API的,也就是实际上它需要跟具体的日志框架结合使用。

由于具体日志框架比较多,而且互相也大都不兼容,日志门面接口要想实现与任意日志框架结合可能需要对应的桥接器,说白了,所谓“桥接器”,不过就是对某套API的伪实现。

这种实现并不是直接去完成API所声明的功能,而是去调用有类似功能的别的API。这样就完成了从“某套API”到“别的API”的转调。

添加桥接依赖:

org.slf4j

slf4j-log4j12

3)测试代码:

importorg.slf4j.Logger;

importorg.slf4j.LoggerFactory;

publicclassLog4jSif4jTest{

publicstaticvoidmain(String[]args){

Logger logger=LoggerFactory.getLogger(Log4jSif4jTest.class);

logger.info(logger.getClass().getName());

logger.info("门面框架Sif4j整合Log4j输出");

}

}

4)结果输出:

2、JCL门面框架+JUL实现

1)引入桥接依赖:

commons-logging

commons-logging

2)测试代码:

importorg.apache.commons.logging.Log;

importorg.apache.commons.logging.LogFactory;

publicclassJCLJULTest{

publicstaticvoidmain(String[]args){

Log log=LogFactory.getLog(JCLJULTest.class.getName());

log.info(log.getClass());

log.info("门面框架JCL整合JUL输出");

}

}

3)输出结果:

此时,不是红色字体的JCL依赖。

原因是:JCL动态查找机制进行日志实例化,执行顺序为

commons-logging.properties>系统环境变量>log4j>jul>simplelog>nooplog

故添加commons-logging.properties。

org.apache.commons.logging.Log = org.apache.commons.logging.impl.Jdk14Logger

4)重新运行,输出结果:

五、合整合日志门面为slf4j

1、需求场景:

若项目在开发过程中,不同开发小组使用了不同的日志门面和日志实现系统,如第四节中JCL+JUL和Slf4j+Log4j的实现模式,先在为了统一用Slf4j这个门面系统,又不想对原有JCL+JUL模式的代码进行修改,该如何操作?

2、采用适配器

官方流图如下:

对于JCL门面来说,要想转换成Slf4j,只需要引入JCL的适配器,引入jcl-over-slf4j 的jar包:

org.slf4j

jcl-over-slf4j

3、依旧使用第四章节的代码

importorg.apache.commons.logging.Log;

importorg.apache.commons.logging.LogFactory;

publicclassJCLJULTest{

publicstaticvoidmain(String[]args){

Log log=LogFactory.getLog(JCLJULTest.class.getName());

log.info(log.getClass());

log.info("门面框架JCL整合JUL输出");

}

}

4、结果输出

此时,可见日志类已经变成了Slf4j。

六、总结

1、对于spring框架,默认使用JCL门面以JUL作为日志框架输出,演示代码见第四章第二小节,结构图如下:

2、若项目采用Slf4j门面以Log4j作为日志框架输出,演示代码见第四章第一小节,结构图如下:

3、为了整合,使spring以log4j2的日志框架进行输出,需要使用jcl-over-slf4j适配器,演示代码见第五章,结构图如下:

你可能感兴趣的:(从原理到实践彻底搞懂 Java 日志系统)