Un article pour comprendre la relation de pont entre le système de journalisation Java et SLF4J

1. Histoire du système de journalisation Java

On peut dire que les composants liés au journal Java sont variés, chacun avec ses propres mérites. En plus de divers frameworks de log, tels que log4j, logback, etc., il existe diverses façades de log, qui sont éblouissantes. Cet article va trier l'historique de développement du framework de journalisation Java et vous apporter le diagramme de pontage de la façade de journalisation SLF4J la plus populaire.

1.1 System.out 与 System.error

Initialement, les développeurs utilisaient System.out.println()ou pour imprimer les journaux System.err.println().

Parmi eux, System.outest le flux de sortie standard (stdout), System.errest le flux de sortie d'erreur (stderr). Ainsi, dans de nombreux IDE, System.errla couleur de la police de sortie est rouge, en guise d'avertissement ; System.outc'est la couleur normale.

Par exemple, pour le code suivant :

public class MyLog {
    public static void main(String[] args) {
        System.out.println("this is out.");
        System.err.println("this is err.");
    }
}
复制代码

Lorsque nous redirigeons la sortie, elle System.outcorrespond System.errà un flux de sortie Linux différent.

# 1. 直接运行代码,将在控制台看到两个输出
$ javac MyLog.java && java MyLog
this is out.
this is err.

# 2. 重定向错误输出流,将在控制台看到“this is out.”,在error.log中看到“this is error.”
$ javac MyLog.java && java MyLog 2>error.log
this is out.

# 3. 重定向标准输出流,将在控制台看到“this is error.”,在info.log中看到“this is out.”
$ javac MyLog.java && java MyLog >info.log 
this is err.

# 4. 同时重定向stdout和stderr,将在info.log中看到两个输出
$ javac MyLog.java && java MyLog >info.log 2>&1
# 控制台无输出
复制代码

Pour cette raison, si vous utilisez les deux méthodes pour imprimer dans le code, les résultats que nous voyons sur la console sont en fait incohérents avec l'ordre du code.

public static void main(String[] args) throws InterruptedException {
    for (int i = 0; i < 400; i++) {
        System.out.println(i);
        System.err.println(i);
    }
}
复制代码

Résultat de sortie :image.png

1.1.1 Problèmes existants

  1. La sortie du journal est synchrone et nécessite l'acquisition d'un verrou. Lorsqu'une grande quantité de sortie de journal se produit, une concurrence de verrouillage se produit, ce qui peut bloquer le thread.
  2. Différents développeurs définiront différents formats de journaux.Lorsqu'une grande équipe de développement ou de nombreux packages jar tiers sont introduits, les journaux imprimés seront chaotiques.

1.2 Naissance de Log4J

Afin d'éviter la confusion ci-dessus, l'équipe du projet EU SEMPER (Secure Electronic Marketplace for Europe) a commencé en 1996 à développer une API de suivi des programmes. Après d'innombrables optimisations, cette API a évolué vers le framework Log4J et l'a rendue 1999年publique.

Plus tard, Log4J est devenu membre du projet Apache Foundation, et son auteur Ceki a également rejoint Apache.

1.3 Naissance de JUL

Sun公司的Java的母公司。2002年,JDK1.4版本发布,Sun首次推出了日志库 java.util.logging (JUL)包,其实现基本模仿了Log4j的实现。不过此时 Log4J已经比较成熟,所以在开发者的选择上,Log4J占有一定优势。

1.4 JCL诞生

如果一个系统仅由一个开发者开发,那开发者可以选择只一个合适的日志库。但如果项目要引入各种第三方jar包,而不同jar包可能使用的是不同的日志库(例如Log4J或者JUL),这样一来打印的日志依旧是混杂着不同格式的。

故在2002年,Apache推出了日志门面JCL(Jakarta Commons Logging,虽然后来改名成了Apache Commons Logging,但依旧习惯于称它为JCL)。JCL只是定义了一套日志接口(其内部也提供一个Simple Log的简单实现),支持运行时动态加载日志组件的实现,这样,在你应用代码里,只需调用JCL的接口,底层实现可以是Log4j,也可以是JUL。

但由于Maven和Tomcat历史版本的设计原因,倘若开发者引入依赖时遗漏了JCL的jar包,可能会导致Tomcat内存泄漏,迫使程序跑着跑着就崩溃了。虽然在后续的版本中修复了这个问题,但人们对它还是心有余悸。

1.5 SLF4J/Logback 诞生

2006年,Ceki因无法适应Apache的工作,选择了离开。之后,Ceki便着手开发了Logback日志框架和SLF4J日志门面。

SLF4J与JCL是直接的竞争对手。但不同于JCL,是在运行时动态加载日志组件的,SLF4J需要开发者明确地引入桥接包。并且,Ceki自己实现了一系列的桥接包来帮助SLF4J与其他日志库建立关系。

这样一来,不论第三方jar包使用的是什么日志框架,只要我们应用中合理使用桥接包,都可以转化为统一的日志格式。

1.6 Log4j2

Apache眼看有被 Logback 反超的势头,于2012年完全重写了Log4j 1.x,推出了Log4J 2.x。Log4j2具有Logback的所有特性,并且也提供了桥接包,故可以使用 JCL 与 SLF4J 的API。

1.7 现状与总结

经过长久的发展,日志组件现状如下:

  • Log4j 1.x直到现在仍被许多项目使用,不过早在2015年,官方已经停止了维护。并且Log4j 1.x最有用的特性之一在Java 9中也无法使用。
  • SLF4J/Logback 到现在仍被众多开发者使用。
  • Log4j 2.x也提供了桥接包,这样开发者可以使用 JCL 或者 SLF4J 日志门面作为API,后台使用Log4j2为日志框架。
  • JCL仍然存在,但自2014年后也从未更新过。

image.png

二、SLF4J桥接包

日志框架与桥接包众多,笔者特地画了张图,可以清晰地表示主流日志门面与日志框架所有关系。 SLF4J桥接包关系图

  1. 其中方框里的是jar包,可直接在Maven中心仓库中搜索,引入需要的版本。
  2. 在Logback中,并没有对应的桥接器,因为是同一个作者写的,所以已经适配SLF4J了。
  3. 注意在使用slf4j日志门面之后,只能指定一个slf4j的适配库。在引入其他包的时候,如果有日志冲突,需要使用 <exclusion>...</exclusion> 来去掉包。
  4. 一个日志框架对应的适配器和桥接器不能同时引入,否则会类似于死循环,导致栈溢出 StackOverflowError。例如slf4j-log4j12 和 log4j-over-slf4j 同时引入。

三、实践例子

假设现在有3个项目A、B、C。 其中A使用 JCL的简单实现;B使用 Log4j;C使用 Log4j2。现在我们想统一到使用 Log4j2,怎么办呢?

只需要在SLF4J桥接包关系图中找连接关系,对应到 Log4j2即可。

shuiyin2.jpg

问题来了,为什么我们项目C不直接使用 Log4j2的包,而是通过SLF4J这一层呢?因为实际开发中,推荐使用日志门面去调用日志接口,这样在转换日志实现时,仅需引入适配器即可,更加方便与高效。

四、参考


你的点赞和关注是对我最大的鼓励!
我的往期文章:
浅析认证授权的三种方式(session、token、JWT)
浅析如何保证数据库与缓存一致性

Je suppose que tu aimes

Origine juejin.im/post/7078293070751465508
conseillé
Classement