JavaログシステムとSLF4Jのブリッジ関係を理解するための1つの記事

1.Javaロギングシステムの歴史

Javaログ関連のコンポーネントはさまざまであると言え、それぞれにメリットがあります。log4j、logbackなどのさまざまなログフレームワークに加えて、まばゆいばかりのさまざまなログファサードがあります。この記事では、Javaロギング・フレームワークの開発履歴を整理し、最も人気のあるSLF4Jロギング・ファサードのブリッジ図を紹介します。

1.1System.out与System.error

当初、開発者はSystem.out.println()またはログを印刷するために使用していましSystem.err.println()た。

その中にSystem.outは、標準出力ストリーム(stdout)、System.errエラー出力ストリーム(stderr)があります。そのため、多くのIDEではSystem.err、警告として出力フォントの色は赤です。System.outこれは通常の色です。

たとえば、次のコードの場合:

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

出力をリダイレクトすると、別のLinux出力ストリームにSystem.out対応します。System.err

# 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
# 控制台无输出
复制代码

このため、両方の方法を使用してコードを印刷すると、コンソールに表示される結果は実際にはコードの順序と一致しません。

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

出力結果:image.png

1.1.1既存の問題

  1. ログ出力は同期的であり、ロックを取得する必要があります。大量のログ出力が発生すると、ロックの競合が発生し、スレッドがブロックされる可能性があります。
  2. さまざまな開発者がさまざまなログ形式を定義します。大規模な開発チームまたは多くのサードパーティのjarパッケージが導入されると、印刷されるログは無秩序になります。

1.2Log4Jの誕生

上記の混乱を避けるために、EU SEMPER(Secure Electronic Marketplace for Europe)プロジェクトチームは、プログラム追跡APIの開発を1996年に開始しました。数え切れないほどの最適化の後、このAPIはLog4Jフレームワークに進化し、1999年公開されました。

その後、Log4JはApache Foundationプロジェクトのメンバーになり、その作者であるCekiもApacheに参加しました。

1.37月の誕生

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)
浅析如何保证数据库与缓存一致性

おすすめ

転載: juejin.im/post/7078293070751465508