Java日志框架的发展历史,你不想了解一下吗

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介: Java日志框架的发展历史,你不想了解一下吗

1️⃣Java日志生态图


首先来看一张Java日志生态图,其中蓝色部分是日志实现,粉红色部分是日志门面,白色部分是相关的绑定包和桥接包。(关于Java日志框架的详细介绍请期待我的下一篇文章)

ac3aad3ce75f4003996e8916c657ff8b.png


初次看这张图肯定是比较懵逼的,各种包桥接过去桥接过来,绑定来绑定去,都是些啥玩意儿?


虽然看这张Java日志生态图很乱,很难梳理清楚,可能名字都不是很好记,但是如果我们从Java日志系统本身的发展历史去了解一下,可能就会明白为什么会出现图中这种情况了!


Java日志框架整体经历了如下图所示的演变历史,接下来就来看看吧~


36e2e7ad02ad4d26a6ad37dafb5c8580.png



2️⃣System.out与System.err


这应该是最早的日志记录方式吧,缺点是:


不灵活也不可以配置,要么就是全部打印,要么就是全部不打印,没有一个统一的日志级别;

产生大量的IO操作同时在生产环境中无法合理的控制是否需要输出;

输出的内容不能保存到文件;只打印在控制台,打印完就过去了,也就是说除非你一直盯着程序跑;

无法定制化,且日志粒度不够细。


3️⃣Log4j


在1996年初,E.U.SEMPER(欧洲安全电子市场)项目决定编写自己的跟踪API,最后该API演变为Log4j,Log4j日志软件包一经推出就备受欢迎,当然这里必须要提到一个人,就是Log4j的主要贡献者,这个大佬:Ceki Gülcü。


这位大佬应该不能仅仅用大佬来称呼了,可能应该叫巨佬了!讲到后面你就慢慢明白了,后来Log4j成为了Apache基金会项目中的一员,同时Log4j的火爆,让Log4j一度成为业内日志标杆。(据说Apache基金会还曾经建议Sun引入Log4j到java的标准库中,但是Sun拒绝了)


4️⃣JUL


果然Sun有“自己的考虑”,2002年2月Java1.4发布,Sun竟然推出了自己的日志库Java Util Logging(JUL),其实很多日志的实现思想也都是仿照Log4j,毕竟Log4j先出来很多年了,已经很成熟了此时,这两个日志工具打架,显然Log4j是更胜一筹。


神仙打架其实就是互相竞争,Sun心里可能在想,不就是做个日志工具嘛,谁不会!当然好景不长。


5️⃣JCL


Apache: 玩编程,谁玩的过我!你不让我成为JDK标准,我就自己成为日志标准!


于是JUL刚出来不久,2002年8月Apache又推出了日志接口Jakarta Commons Logging(JCL),也就是日志抽象层,当然也提供了一个默认实现Simple Log,这野心很大,想一统日志抽象(就像以前的JDBC一统数据库访问层),让日志产品去实现它的抽象,这样只要你的日志代码依赖的是JCL的接口,你就可以很方便的在Log4j和JUL之间做切换,当时日志领域大概是这样的结构,当然也还是方便理解的,也很优雅。


bda3dc2c67ae40a58ff5c665510380b0.png

但是好景不长,在使用过程中,虽然现在日志系统在JCL的统一下很优雅,很美好,但大家发现了JCL还不够好,有些人甚至认为JCL造成的问题比解决的问题还多…JUL主要有三个缺点:(1)效率较低;(2)容易引发混乱;(3)使用了自定义ClassLoader的程序中,使用JCL会引发内存泄露。


2aa1bf771a0c41e7ba9ef9f9cdd6cfc6.png

6️⃣Slf4j


所以大佬出现,Ceki Gülcü(也就是Log4j的作者)由于一些原因离开了Apache,之后觉得JCL不好,于是于2005年自己撸出一个新工程,也就是一套新日志接口(有得也叫日志门面):Slf4j(Simple Logging Facade for Java),感觉粗来了么,这战争的硝烟,明显这个Slf4j是直指JCL啊,但是后面确实也证明了Slf4j是要比JCL在很多地方更优秀。


Ceki Gülcü: 玩接口,我一个人就是一支军队!


但是由于Slf4j出来的较晚,而且还只是一个日志接口,所以之前已经出现的日志产品,如JUL和Log4j都是没有实现这个接口的,所以尴尬的是光有一个接口,没有实现的产品也是很憋屈啊,就算开发者想用Slf4j也是用不了,这时候,大佬发话了。


Ceki Gülcü: 别急,我早帮你们想好了,要让Sun或者Apache这两个庞然大物来实现我的接口,太南啦,老铁,但…我帮你们实现,不就完了么。


于是大佬Ceki Gülcü撸出了之前提到的桥接包,通过桥接包来帮助Slf4j接口与其他日志库建立关系,这种方式称桥接设计模式。代码使用Slf4j接口,就可以实现日志的统一标准化,后续如果想要更换日志实现,只需引入Slf4j与相关的桥接包,再引入具体的日志标准库即可。


58a51435e48641c5aaad130ed41fbe6e.png


大佬提供了桥接包,于是日志系统现在有了这样的结构:


07726ac025ea407cb4720be8b26f685d.png


但是其实之前很多Java应用应该依赖的JCL,所以光有日志产品桥接包,好像还不够。

Ceki Gülcü: 没问题,不就是桥接包么,我写,我来证明Slf4j是最完美的。

于是有了JCL的桥接包:

43908a94ca9a4570af35d4965651896e.png


相当于此时的桥接包就是分了两种场景:(1)之前Java应用用的日志接口(如JCL);(2)之前Java应用用的日志产品(如Log4j)。


那我们如果再考虑一下这种场景呢?

假设你的Java应用使用了Spring的第三方的框架,但是假设Spring默认用JCL,并且最终用的JUL打印的日志,但是你的系统使用了Slf4j作为日志接口,日志产品使用了Log4j,那不出意外的话…你将有两种日志输出,两种日志的打印方式不统一,到时候解决bug的时候就很恼火,而且配置日志的配置文件还需要两份。


a21f9c1db02c41808447ae9f1dd0fe37.png


所以为了方便统一应用中的所有日志,大佬发话了。

Ceki Gülcü: 没事,大家都选择用Slf4j统一吧,我来帮大家统一,没有事是桥接包解决不了的,有的话,那就再来个。


da4a07320e1e4a6590f23f09589c8d2e.png

当然此时这种场景也是符合之前说的两种情况的,因此现在日志系统大体应该是这样的:


cb41b069e2f04d408e374da5f35ca483.png

但是…但是看后边吧,大佬毕竟是大佬,Log4j不就是自己写的么,所以最清楚Log4j缺点的人也正是他。


7️⃣Logback


Ceki Gülcü 巨佬觉得市场上的日志标准库都是间接实现Slf4j接口,也就是说每次都需要配合桥接包,也就是之前的日志产品都不是正统的Slf4j的实现。


因此在2006年,Ceki Gülcü 基于Slf4j接口写出了Logback日志标准库,做为Slf4j接口的默认实现,Logback也十分给力,在功能完整度和性能上超越了所有已有的日志标准库,其根本原因还在于,随着用户体量的提升,Log4j无法满足高性能的要求,成为应用的性能瓶颈。而且当时大佬还专门写了一篇文章:

1bfb8a6f37f946c4944049c0889c17c3.png

是不是这太针对了…哈哈哈哈,就是这么无情,当然都是他写的,他肯定是最清楚这两者实现的区别。

毋庸置疑,Logback是完美实现了Slf4j,于是现在日志系统变成了:


430e67e21b0f4d05bc31226c9ff7affb.png

现在有了2个日志接口,3个日志产品,大家也都看起来相安无事。但…Slf4j+Logback的模式,显然很冲击JCL+Log4j,并且本身Logback确实比Log4j性能更优,设计更为合理,所以,老东家Apache可就坐不住了。


8️⃣Log4j2


在2012年,Apache直接推出新项目,不是Log4j1.x升级,而是新项目Log4j2,因为Log4j2是完全不兼容Log4j1.x的。


并且很微妙的,Log4j2几乎涵盖Logback所有的特性(这不是对着干是啥~而且还有抄袭的嫌疑哈哈哈),更甚者的Log4j2也搞了分离的设计,分化成log4j-api和log4j-core,这个log4j-api也是日志接口,log4j-core才是日志产品。


现在我们可有了3个日志接口,以及4个日志产品。当然Apache也知道该做啥,为了让大家可以接入自己的Log4j2,那不就是桥嘛,Apache也麻溜的推出了它的桥接包,所以日志系统变成了现在的局面:

bac22c63d9844f63a253bca555602f24.png


9️⃣思考


了解了日志的发展历史,我们再回过头来思考一下,如果,你的系统在选择日志方案的时候,如何抉择呢?毕竟有3个日志接口,以及4个日志产品。


显然第一点是使用日志接口的API而不是直接使用日志产品的API。这一条也是必须的,也是符合依赖倒置原则的,我们应该依赖日志的抽象,而不是日志的实现。

日志产品的依赖只添加一个。当然也这个也是必须的,依赖多个日志产品,只会让自己的应用处理日志显得更复杂,不可统一控制。

把日志产品的依赖设置为Optional和runtime scope。其中Optional是为了依赖不会被传递,比如别的人引用你这个jar,就会被迫使用不想用的日志依赖;而scope设置为runtime,是可以保证日志的产品的依赖只有在运行时需要,编译时不需要,这样,开发人员就不会在编写代码的过程中使用到日志产品的API了。


  <dependency>
      <groupId>org.apache.logging.log4j</groupId>
      <artifactId>log4j-core</artifactId>
      <version>${log4j.version}</version>
      <optional>true</optional>
  </dependency>
    <dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-slf4j-impl</artifactId>
        <version>${log4j.version}</version>
        <scope>runtime</scope>
    </dependency>


相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
相关文章
|
11天前
|
XML 安全 Java
【日志框架整合】Slf4j、Log4j、Log4j2、Logback配置模板
本文介绍了Java日志框架的基本概念和使用方法,重点讨论了SLF4J、Log4j、Logback和Log4j2之间的关系及其性能对比。SLF4J作为一个日志抽象层,允许开发者使用统一的日志接口,而Log4j、Logback和Log4j2则是具体的日志实现框架。Log4j2在性能上优于Logback,推荐在新项目中使用。文章还详细说明了如何在Spring Boot项目中配置Log4j2和Logback,以及如何使用Lombok简化日志记录。最后,提供了一些日志配置的最佳实践,包括滚动日志、统一日志格式和提高日志性能的方法。
117 30
【日志框架整合】Slf4j、Log4j、Log4j2、Logback配置模板
|
6天前
|
JSON Java Apache
非常实用的Http应用框架,杜绝Java Http 接口对接繁琐编程
UniHttp 是一个声明式的 HTTP 接口对接框架,帮助开发者快速对接第三方 HTTP 接口。通过 @HttpApi 注解定义接口,使用 @GetHttpInterface 和 @PostHttpInterface 等注解配置请求方法和参数。支持自定义代理逻辑、全局请求参数、错误处理和连接池配置,提高代码的内聚性和可读性。
|
27天前
|
算法 Java 数据处理
从HashSet到TreeSet,Java集合框架中的Set接口及其实现类以其“不重复性”要求,彻底改变了处理唯一性数据的方式。
从HashSet到TreeSet,Java集合框架中的Set接口及其实现类以其“不重复性”要求,彻底改变了处理唯一性数据的方式。HashSet基于哈希表实现,提供高效的元素操作;TreeSet则通过红黑树实现元素的自然排序,适合需要有序访问的场景。本文通过示例代码详细介绍了两者的特性和应用场景。
36 6
|
27天前
|
存储 Java
深入探讨了Java集合框架中的HashSet和TreeSet,解析了两者在元素存储上的无序与有序特性。
【10月更文挑战第16天】本文深入探讨了Java集合框架中的HashSet和TreeSet,解析了两者在元素存储上的无序与有序特性。HashSet基于哈希表实现,添加元素时根据哈希值分布,遍历时顺序不可预测;而TreeSet利用红黑树结构,按自然顺序或自定义顺序存储元素,确保遍历时有序输出。文章还提供了示例代码,帮助读者更好地理解这两种集合类型的使用场景和内部机制。
37 3
|
27天前
|
存储 Java 数据处理
Java Set接口凭借其独特的“不重复”特性,在集合框架中占据重要地位
【10月更文挑战第16天】Java Set接口凭借其独特的“不重复”特性,在集合框架中占据重要地位。本文通过快速去重和高效查找两个案例,展示了Set如何简化数据处理流程,提升代码效率。使用HashSet可轻松实现数据去重,而contains方法则提供了快速查找的功能,彰显了Set在处理大量数据时的优势。
32 2
|
29天前
|
存储 算法 Java
Java Set因其“无重复”特性在集合框架中独树一帜
【10月更文挑战第14天】Java Set因其“无重复”特性在集合框架中独树一帜。本文深入解析Set接口及其主要实现类(如HashSet、TreeSet)如何通过特定的数据结构(哈希表、红黑树)确保元素唯一性,并提供最佳实践建议,包括选择合适的Set实现类和正确实现自定义对象的`hashCode()`与`equals()`方法。
28 3
|
15天前
|
人工智能 前端开发 Java
基于开源框架Spring AI Alibaba快速构建Java应用
本文旨在帮助开发者快速掌握并应用 Spring AI Alibaba,提升基于 Java 的大模型应用开发效率和安全性。
基于开源框架Spring AI Alibaba快速构建Java应用
|
16天前
|
消息中间件 Java 数据库连接
Java 反射最全详解 ,框架设计必掌握!
本文详细解析Java反射机制,包括反射的概念、用途、实现原理及应用场景。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
Java 反射最全详解 ,框架设计必掌握!
|
23天前
|
前端开发 Java 数据库连接
Spring 框架:Java 开发者的春天
Spring 框架是一个功能强大的开源框架,主要用于简化 Java 企业级应用的开发,由被称为“Spring 之父”的 Rod Johnson 于 2002 年提出并创立,并由Pivotal团队维护。
43 1
Spring 框架:Java 开发者的春天
|
22天前
|
SQL Java 关系型数据库
java连接mysql查询数据(基础版,无框架)
【10月更文挑战第12天】该示例展示了如何使用Java通过JDBC连接MySQL数据库并查询数据。首先在项目中引入`mysql-connector-java`依赖,然后通过`JdbcUtil`类中的`main`方法实现数据库连接、执行SQL查询及结果处理,最后关闭相关资源。