从原理到实践彻底搞懂 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为开源组件,使用前需要添加依赖:

<dependency>

<groupId>log4j</groupId>

<artifactId>log4j</artifactId>

</dependency>

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的核心依赖:

<dependency>

<groupId>org.slf4j</groupId>

<artifactId>slf4j-api</artifactId>

</dependency>

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

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

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

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

添加桥接依赖:

<dependency>

<groupId>org.slf4j</groupId>

<artifactId>slf4j-log4j12</artifactId>

</dependency>

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)引入桥接依赖:

<dependency>

<groupId>commons-logging</groupId>

<artifactId>commons-logging</artifactId>

</dependency>

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包:

<dependency>

<groupId>org.slf4j</groupId>

<artifactId>jcl-over-slf4j</artifactId>

</dependency>

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适配器,演示代码见第五章,结构图如下:

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,122评论 6 505
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,070评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,491评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,636评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,676评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,541评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,292评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,211评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,655评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,846评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,965评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,684评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,295评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,894评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,012评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,126评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,914评论 2 355

推荐阅读更多精彩内容