写点什么

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

  • 2019-09-24
  • 本文字数:3315 字

    阅读完需:约 11 分钟

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-09-24 18:191165

评论

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

DevStream 成为 CNCF Sandbox 项目啦!- 锣鼓喧天、鞭炮齐鸣、红旗招展、忘词了。

胡说云原生

开源 cncf DevStream

元宇宙来袭的五个趋势

CECBC

通过DAO的现状,看Web3最具影响力的基础设施M-DAO

小哈区块

【用户文章转载】版本管理这件事,没有偏执,惟有极致

龙智—DevSecOps解决方案

游戏开发 版本管理 CI工具链 周版本制度

官宣!Apache Doris 从 Apache 基金会毕业,正式成为 Apache 顶级项目!

SelectDB

Apache 数据库 apache doris

如何针对海外不同地区进行音视频自动化测试?丨Dev for Dev 专栏

声网

自动化测试 Dev for Dev

【LeetCode】数组中的 k-diff 数对Java题解

Albert

LeetCode 6月月更

转转统一权限系统的设计与实现(设计篇)

转转技术团队

权限系统 rbac

研究uni-app的第五天

恒山其若陋兮

6月月更

力扣每日一练之二维数组上篇Day4

京与旧铺

6月月更

vue prop传递数据

小恺

6月月更

微服务如何拆分

阿泽🧸

微服务 6月月更

V1签名校验

北洋

Andriod 6月月更

阿里云智能编码插件进行了一个上新大动作

阿里云云效

云计算 阿里云 云原生 代码

频频破圈,走向百业:大模型的毕业季

脑极体

一文带你认识HTML

未见花闻

6月月更

数字人民币预付式消费的监管之道,智能合约能不能解决所有问题?

CECBC

Java Core 「9」J.U.C 同步工具类-1

Samson

学习笔记 Java core 6月月更

IP核是什么?有什么类型?半导体IP核全攻略

龙智—DevSecOps解决方案

知识产权 半导体 芯片开发 半导体IP核 IP核管理

再仿个人主页来看 GetX 和 Provider 之间的 PK

岛上码农

flutter ios 前端 安卓 6月月更

八大误区,逐个击破(3):在云上,变更和数据的管理都不足为虑

龙智—DevSecOps解决方案

atlassian云版 版本选择 迁移上云

盘点一些好用且小众的 Markdown 编辑器

宇宙之一粟

markdown编辑器 6月月更

leetcode 198. House Robber 打家劫舍(中等)

okokabcd

LeetCode 动态规划 数据结构与算法

数据质量管理

奔向架构师

数据治理 数据管理 6月月更

“多元化”通证经济模型:DAO的神经和血液

CECBC

揭秘攻防演练中红队需要什么样的人才

穿过生命散发芬芳

6月月更 攻防演练

95后阿里P7晒出工资单:狠补了这些个技术栈,真的香啊

Java全栈架构师

Java 程序员 面试 架构师 Java面试题

如何防止NFT行业被污名化?

CECBC

网站制作FAQ页面必要性及方法

小炮

一款可以实现内网脱机分享文档的接口测试软件

Xd

Java 数据库 后端 API 接口测试软件

Java常用日志框架介绍(下)_文化 & 方法_圣卡西_InfoQ精选文章