Java日志全解析(上) – 源流「建议收藏」

作为Java程序员,幸运的是,Java拥有功能和性能都非常强大的日志库;不幸的是,这样的日志库有不止一个——相信每个人都曾经迷失在JUL(JavaUtilLog),JCL(CommonsLogging),Log4j,SLF4J,Logback,Log4j2等等的迷宫中。在我见过的绝大多数项目中,都没有能够良好的配置和使用日志库。

大家好,又见面了,我是你们的朋友全栈君。

原文链接: https://zhuanlan.zhihu.com/p/24272450, 已获得作者授权转载.

作为Java程序员,幸运的是,Java 拥有功能和性能都非常强大的日志库;不幸的是,这样的日志库有不止一个——相信每个人都曾经迷失在JUL(Java Util Log), JCL(Commons Logging), Log4j, SLF4J, Logback,Log4j2 等等的迷宫中。在我见过的绝大多数项目中,都没有能够良好的配置和使用日志库。

这篇文章先讲述Java常见日志库的历史和关系,后续会讲日志使用的最佳实践。让我们从头(Java Util Log)开始说起吧。

Java Util Log

简称JUL,是JDK 中自带的log功能。虽然是官方自带的log lib,JUL的使用确不广泛。主要原因:

  1. JUL从JDK1.4 才开始加入(2002年),当时各种第三方log lib已经被广泛使用了
  2. JUL早期存在性能问题,到JDK1.5上才有了不错的进步,但现在和Logback/Log4j2相比还是有所不如
  3. JUL的功能不如Logback/Log4j2等完善,比如Output Handler就没有Logback/Log4j2的丰富,有时候需要自己来继承定制,又比如默认没有从ClassPath里加载配置文件的功能

Log4j 1.x

Log4j 是在 Logback 出现之前被广泛使用的 Log Lib, 由 Gülcü 于2001年发布,后来成为Apache 基金会的顶级项目。Log4j 在设计上非常优秀,对后续的 Java Log 框架有长久而深远的影响,也产生了Log4c, Log4s, Log4perl 等到其他语言的移植。Log4j 的短板在于性能,在Logback 和 Log4j2 出来之后,Log4j的使用也减少了。

Commons Logging

简称JCL,是Apache下面的项目。JCL 是一个Log Facade,只提供 Log API,不提供实现,然后有 Adapter 来使用 Log4j 或者 JUL 作为Log Implementation。

就像之前所说,JDK现在带了自己的JUL,然后又有第三方的 Log4j 等日志库存在,不同的项目可能各自使用了不同的日志库。如果你的项目依赖的其他 lib 各自使用了不同的日志库,你想控制日志行为,就需要针对每个日志库都写一个配置文件,是不是很酸爽?

然后这个时候 JCL 就出现了。在程序中日志创建和记录都是用JCL中的接口,在真正运行时,会看当前ClassPath中有什么实现,如果有Log4j 就是用 Log4j, 如果啥都没有就是用 JDK 的 JUL。

这样,在你的项目中,还有第三方的项目中,大家记录日志都使用 JCL 的接口,然后最终运行程序时,可以按照自己的需求(或者喜好)来选择使用合适的Log Implementation。如果用Log4j, 就添加 Log4j 的jar包进去,然后写一个 Log4j 的配置文件;如果喜欢用JUL,就只需要写个 JUL 的配置文件。如果有其他的新的日志库出现,也只需要它提供一个Adapter,运行的时候把这个日志库的 jar 包加进去。

Java日志全解析(上) - 源流「建议收藏」

到这个时候一切看起来都很简单,很美好。接口和实现做了良好的分离,在统一的JCL之下,不改变任何代码,就可以通过配置就换用功能更强大,或者性能更好的日志库实现。

这种简单美好一直持续到SLF4J出现。

SLF4J/Logback

SLF4J(The Simple Logging Facade for Java) 和 Logback 也是Gülcü 创立的项目,其创立主要是为了提供更高性能的实现。其中,SLF4j 是类似于JCL 的Log Facade,Logback 是类似于Log4j 的 Log Implementation。

之前已经说过,Apache 有了个JCL,用来做各种Log lib统一的接口,如果 Gülcü 要搞一个更好的 Log Implementation 的话,直接写一个实现就好了,为啥还要搞一个和SLF4J呢?

原因是Gülcü 认为JCL的API设计得不好,容易让使用者写出性能有问题的代码。

比如在用 JCL 输出一个 debug 级别的 log:

logger.debug("start process request, url:" + url);

这个有什么问题呢?一般生产环境 log 级别都会设到 info 或者以上,那这条 log 是不会被输出的。然而不管会不会输出,这其中都会做一个字符串连接操作,然后生产一个新的字符串。如果这条语句在循环或者被调用很多次的函数中,就会多做很多无用的字符串连接,影响性能。

所以 JCL 的最佳实践推荐这么写:

if (logger.isDebugEnabled()) { 
     
    logger.debug("start process request, url:" + url);
}

然而开发者常常忽略这个问题或是觉得麻烦而不愿意这么写。所以SLF4J提供了新的API,方便开发者使用:

logger.debug("start process request, url:{}", url);

这样的话,在不输出 log 的时候避免了字符串拼接的开销;在输出的时候需要做一个字符串format,代价比手工拼接字符串大一些,但是可以接受。

而 Logback 则是作为 Log4j 的继承者来开发的,提供了性能更好的实现,异步 logger,Filter等更多的特性。

现在事情变复杂了。我们有了两个流行的 Log Facade,以及三个流行的 Log Implementation。Gülcü 是个追求完美的人,他决定让这些Log之间都能够方便的互相替换,所以做了各种 Adapter 和 Bridge 来连接:

Java日志全解析(上) - 源流「建议收藏」

可以看到甚至 Log4j 和 JUL 都可以桥接到SLF4J,再通过 SLF4J 适配到到 Logback!

在这里需要注意不能搞出循环的桥接,比如下面这些依赖就不能同时存在:

  1. jcl-over-slf4j 和 slf4j-jcl
  2. log4j-over-slf4j 和 slf4j-log4j12
  3. jul-to-slf4j 和 slf4j-jdk14

总感觉事情在变得更麻烦呢!

Log4j2

现在有了更好的 SLF4J 和 Logback——你会想事情到这里总该了解了吧,让他们慢慢取代JCL 和 Log4j 好了。

然而维护 Log4j 的人不这样想,他们不想坐视用户一点点被 SLF4J /Logback 蚕食,继而搞出了 Log4j2。

Log4j2 和 Log4j1.x 并不兼容,设计上很大程度上模仿了 SLF4J/Logback。

首先 Log4j2 也做了 Facade/Implementation 分离的设计,分成了 log4j-api 和 log4j-core。

然后 log4j-api 学习了 SLF4j 的参数传入方式,并且在 Java8 下支持 Lambda 风格的 Lazy 求值:

logger.debug(() -> "start process request, url:" + url);

再然后 log4j-core 提高了性能(号称比 Logback 还高很多),提供异步 Appender,Filtering 等功能。

再然后他们也决定像SLF4J那样提供各种桥接器来支持替换其他Log Facade / Implementation!

现在好了,我们有了三个流行的Log 接口和四个流行的Log实现,如果画出桥接关系的图来回事什么样子呢?

Java日志全解析(上) - 源流「建议收藏」是不是感觉有点晕呢?同样,在添加依赖的时候,要小心不要搞成循环依赖。

看到这里可能要问了,我们现在究竟应该怎么配置和使用 Java 的日志库呢?请看下回Java日志全解析(下) – 最佳实践

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/126947.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)
blank

相关推荐

  • Ubuntu下Redis密码设置问题及其解决方案[通俗易懂]

    Ubuntu下Redis密码设置问题及其解决方案[通俗易懂]Ubuntu下Redis密码设置问题及其解决方案一、Redis设置密码1.命令行设置密码。2.配置文件设置密码二、遇到问题&解决问题1.无法打开配置文件:2.配置文件密码修改成功点击保存但是却gedit警告:3.gedit配置文件修改密码成功但仍CONFIGGET为空4.注意修改配置文件完成后,一定要重启Redis服务器!叮嘟!最近做项目学习用到了Redis,在刚开始的摸索过程踩…

  • oracle 锁表、解锁的语句

    oracle 锁表、解锁的语句对oracle数据库的表进行update操作的时候,忘了提交,导致后面无法对表格进行数据修改操作。现将网络搜索到的方法粘贴如下,供遇到问题的小伙伴方便查看。如有侵权,请一定告知,本人必将尽快删除。你要知道表锁住了是不是正常锁?因为任何DML语句都会对表加锁。你要先查一下是那个会话那个sql锁住了表,有可能这是正常业务需求,不建议随便KILLsession,如果这个锁表是正常业务你把se…

  • Google云盘的使用「建议收藏」

    Google云盘的使用「建议收藏」GoogleColab简介(参考链接)GoogleColaboratory是谷歌开放的一款研究工具,主要用于机器学习的开发和研究。这款工具现在可以免费使用,但是不是永久免费暂时还不确定。Goog

  • jdbc是数据库连接池么_java的jdbc连接数据库

    jdbc是数据库连接池么_java的jdbc连接数据库JDBC简介,driver类型,JDBC连接,使用技巧;连接,短连接,长连接,连接池;池化思想;配置,基本配置,关键配置,性能配置;连接池工具;druid;HikariCP;flexy-pool;

  • CANoe/CANalyzer诊断功能的深入理解以及CAPL诊断编程实现

    CANoe/CANalyzer诊断功能的深入理解以及CAPL诊断编程实现之前和大家分享了CANoe的基础使用(分析、仿真、测试、诊断),这篇文章将继续深入探讨如何使用CANoe/CANalyzer中的诊断功能。诊断用于在将ECU安装到系统之前或之后配置,维护,支持,控制和扩展ECU,例如,一辆车。诊断通常在请求-响应方案中执行:测试仪(客户端)向…

  • Swift注释

    Swift中的普通注释与Objective-C中的一致,大概分为以下三种//单行注释何问起/ *多行注释hovertree.com*////标记注释1 ht

    2021年12月26日

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号