Java常用日志框架介绍(下)

2019 年 9 月 24 日

Java常用日志框架介绍(下)

8.3 Slf4j 调用过程源码分析,加入 slf4j-api-version.jar,与 Logback 组件

Slf4j 作为门面采用 Logback 作为实现或者采用其它上面提到过的组件作为实现类似,这里只分析采用 Logback 组件作为实现

8.3.1 示例代码

https://github.com/chlsmile/slf4j-logback-demo

8.3.2 pom 核心配置如下

复制代码
1<dependencies>
2 <dependency>
3 <groupId>org.slf4j</groupId>
4 <artifactId>slf4j-api</artifactId>
5 <version>1.7.13</version>
6 </dependency>
7 <!--logback-classic 依赖 logback-core,会自动级联引入 -->
8 <dependency>
9 <groupId>ch.qos.logback</groupId>
10 <artifactId>logback-classic</artifactId>
11 <version>1.2.3</version>
12 </dependency>
13 </dependencies>

8.3.3 程序入口类同上

8.3.4 源码追踪分析

1)、2)、3)、4)同上

5)调用 LoggerFactory 的

findPossibleStaticLoggerBinderPathSet() 方法获取 StaticLoggerBinderPath 集合

6)调用 LoggerFactory 的 bind() 方法的 staticLoggerBinderPathSet 集合对象赋值

7)在 LoggerFactory 的 bind() 方法中调用 loback 包下的 StaticLoggerBinder 创建单例对象

8)在 LoggerFactory 的 bind() 方法中调用 reportActualBinding() 记录日志加载信息

9)LoggerFactory 中 INITIALIZATION_STATE 的值为 SUCCESSFUL_INITIALIZATION,调用 StaticLoggerBinder 的单例对象获取 ILoggerFactory


10)此时 LoggerFactory 中的 getLogger() 方法中获取到的 ILoggerFactory 实际上是 logback jar 下的 LoggerContext

11)此时 LoggerFactory 调用 getLogger() 方法获取到的 Logger 实际上是 logback jar 下的 Logger


8.4 Slf4j 调用过程源码分析,加入 slf4j-api-version.jar,同时加入多种日志实现组件

在项目中如果用 slf4j-api 作为日志门面,有多个日志实现组件同时存在,例如同时存在 Logback,slf4j-log4j12,slf4j-jdk14,slf4j-jcl 四种实现,则在项目实际运行中,Slf4j 的绑定选择绑定方式将有 Jvm 确定,并且是随机的,这样会和预期不符,实际使用过程中需要避免这种情况。

8.4.1 示例代码

https://github.com/chlsmile/slf4j-logback-log4j-demo

8.4.2 pom 核心配置如下

复制代码
1 <dependencies>
2 <dependency>
3 <groupId>junit</groupId>
4 <artifactId>junit</artifactId>
5 <version>4.11</version>
6 </dependency>
7 <dependency>
8 <groupId>org.slf4j</groupId>
9 <artifactId>slf4j-log4j12</artifactId>
10 <version>1.7.25</version>
11 </dependency>
12 <dependency>
13 <groupId>ch.qos.logback</groupId>
14 <artifactId>logback-classic</artifactId>
15 <version>1.2.3</version>
16 </dependency>
17 <dependency>
18 <groupId>org.slf4j</groupId>
19 <artifactId>slf4j-jdk14</artifactId>
20 <version>1.7.25</version>
21 </dependency>
22 <dependency>
23 <groupId>org.slf4j</groupId>
24 <artifactId>slf4j-jcl</artifactId>
25 <version>1.7.25</version>
26 </dependency>
27 </dependencies>

8.4.3 程序入口类同上

8.4.4 源码追踪分析

基本步骤同上,这里只追踪主要不同点

1)追踪 LoggerFactory 的 bind() 方法内部调用 findPossibleStaticLoggerBinderPathSet() 方法后,从 classpath 下 4 个 jar 包内找到 StaticLoggerBinder

2)此时 LoggerFactory 的 bind() 方法内部调用 reportMultipleBindingAmbiguity() 方法,给出警告信息 classpath 下同时存在多个 StaticLoggerBinder,JVM 会随机选择一个 StaticLoggerBinder

9 桥接遗留的 api

在实际环境中我们经常会遇到不同的组件使用的日志框架不同的情况,例如 Spring Framework 使用的是日志组件是 Commons Logging,XSocket 依赖的则是 Java Util Logging。当我们在同一项目中使用不同的组件时应该如果解决不同组件依赖的日志组件不一致的情况呢?现在我们需要统一日志方案,统一使用 Slf4j,把他们的日志输出重定向到 Slf4j,然后 Slf4j 又会根据绑定器把日志交给具体的日志实现工具。Slf4j 带有几个桥接模块,可以重定向 Log4j,JCL 和 java.util.logging 中的 Api 到 Slf4j。

9.1 遗留的 api 桥接方案

9.2 桥接方式参见下图

9.3 使用 Slf4j 桥接要注意事项

在使用 Slf4j 桥接时要注意避免形成死循环,在项目依赖的 jar 包中不要存在以下情况。

9.4 遗留 api 桥接死循环源码分析源码

这里以项目中集成 log4j-over-slf4j 与 slf4j-log4j12 为例,其它组合形成死循环原理相类似。

9.4.1 示例代码

https://github.com/chlsmile/slf4j-Infinite-loop-demo

9.4.2 程序入口类同上

9.4.3 源码追踪分析

基本步骤同上,调用链路 LoggerFactory.getLogger()>LoggerFactory.getILoggerFactory()> LoggerFactory.performInitialization()>LoggerFactory.bind()

1)LoggerFactory.bind() 方法内部调用 StaticLoggerBinder.getSingleton() 获取 StaticLoggerBinder 实例

2)StaticLoggerBinder 调用构造方法内部调用 Log4jLoggerFactory 构造方法创建 ILoggerFactory

3)Log4jLoggerFactory 加载内部 static 代码块,校验出 classpath 下存在 org.apache.log4j.Log4jLoggerFactory,抛出异常

10 排除第三方日志依赖

在实际使用过程中,项目会根据需要引入一些第三方组件,例如常用的 Spring,而 Spring 本身的日志实现使用了 Commons Logging,我们又想使用 Slf4j+Loback 组合,这时候需要在项目中将 Commons Logging 排除掉,通常会用到以下 3 种方案,3 种方案各有利弊,可以根据项目的实际情况选择最适合自己项目的解决方案。

10.1 方案一 采用 maven 的 exclusion 方案

复制代码
1<dependency>
2 <groupId>org.springframework</groupId>
3 <artifactId>spring-core</artifactId>
4 <exclusions>
5 <exclusion>
6 <groupId>commons-logging</groupId>
7 <artifactId>commons-logging</artifactId>
8 </exclusion>
9 </exclusions>
10 <version>${springframework.version}</version>
11</dependency>

这种方案优点是 exclusion 是 maven 原生提供的,不足之处是如果有多个组件都依赖了 commons-logging,则需要在很多处增加,使用起来不太方便。

10.2 方案二 在 maven 声明 commons-logging 的 scope 为 provided

复制代码
1<dependency>
2 <groupId>commons-logging</groupId>
3 <artifactId>commons-logging</artifactId>
4 <version>1.1.1</version>
5 <scope>provided</scope>
6</dependency>
7<dependency>
8 <groupId>org.slf4j</groupId>
9 <artifactId>jcl-over-slf4j</artifactId>
10 <version>1.8.0-beta2</version>
11</dependency>

这种方案在调试代码时还是有可能导致 IDE 将 commons-logging 放置在 classpath 下,从而导致程序运行时出现异常。

10.3 方案三 在 maven 私服中增加类似于 99.0-does-not-exist 这种虚拟的版本号

复制代码
1<dependency>
2 <groupId>commons-logging</groupId>
3 <artifactId>commons-logging</artifactId>
4 <version>99.0-does-not-exist</version>
5</dependency>
6<dependency>
7 <groupId>org.slf4j</groupId>
8 <artifactId>jcl-over-slf4j</artifactId>
9 <version>1.8.0-beta2</version>
10</dependency>

这种方案好处是声明方式比较简单,用 IDE 调试代码时也不会出现问题,不足之处是 99.0-does-not-exist 这种版本是 maven 中央仓库中是不存在的,需要发布到自己的 maven 私服中。

11 总结

由于历史原因 JDK 自身提供的 Log 组件出现的较晚,导致 Jdk 提供 Log 组件时第三方社区的日志组件已经比较稳定成熟。经过多年的发展 Slf4j+Logback 与组合,Commons Logging 与 Log4j 组合两大阵营已经基本成为了 Java 项目开发的标准,建议在新的项目开发中从这两种方案中选择适合自己项目的组合方案。

作者介绍:
圣卡西(企业代号名),目前负责贝壳找房 java 后台开发工作。

本文转载自公众号贝壳产品技术(ID:gh_9afeb423f390)。

原文链接:

https://mp.weixin.qq.com/s/FWnh71eN5jxiu7aBOiUHaA

2019 年 9 月 24 日 18:19 253

评论

发布
暂无评论
发现更多内容

【架构师第二周作业】依赖倒置

浪浪

架构师训练营 第二周 个人感想

且听且吟

极客大学架构师训练营

架构师训练营第二周作业

Geek_2dfa9a

架构师训练营 Week02 学习心得

极客大学架构师训练营

架构师训练营第二周作业

Jerry Tse

极客大学架构师训练营 作业

第2周作业

娄江国

极客大学架构师训练营

架构师训练营第二周总结作业

兔狲

Week 02- 作业二:学习总结

dean

极客大学架构师训练营

第二周作业

晓雷

架构师训练营第二周学习总结

王鑫龙

极客大学架构师训练营

架构师第二周

Tulane

透过本质和发展看编程

拈香(曾德政)

架构师 面向对象设计 极客大学架构师训练营 面向对象设计原则

架构师训练营第二周命题作业

hifly

设计模式 极客大学架构师训练营 UML 依赖倒置原则 接口隔离原则

聊聊面向对象的设计(OOD)原则

Jerry Tse

极客大学架构师训练营 作业

0期架构Week2作业2

Nan Jiang

第2周总结

娄江国

极客大学架构师训练营

江帅帅:精通 Spring Boot 系列 06

奈学教育

Spring Boot

week2作业

慢慢来的比较快

架构师训练营第0期-第2周-命题作业

极客大学架构师训练营

依赖倒置原则

Jeff先生

不懂什么是锁?看看这篇你就明白了

cxuan

Java 并发

江帅帅:精通 Spring Boot 系列 05

奈学教育

Spring Boot

什么是依赖倒置原则

老A

极客大学架构师训练营

架构师课作业-第二周

Tulane

江帅帅:精通 Spring Boot 系列 06

古月木易

Spring Boot

Lesson 2 软件设计原则 心得笔记

edd

架构师训练营第二周命题作业

兔狲

作业

江帅帅:精通 Spring Boot 系列 05

古月木易

Spring Boot

架构师训练营——第二周总结

jiangnanage

架构师训练营-第二周学习总结

牛牛

极客大学架构师训练营 学习总结

打造个人品牌的意义

七镜花园-董一凡

发展 求职

Java常用日志框架介绍(下)-InfoQ