写点什么

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:19956

评论

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

数据结构学习心得

程李文华

第6周总结

andy

CAP Theorem

dongge

区块链扩张路径变局:从技术比拼转向生态落地

CECBC

第六周总结

秦宝齐

作业

第6周作业

andy

第六周作业

秦宝齐

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

博睿宏远获颁“2020开发与技术企业服务奖”

博睿数据

运维自动化 开发工具 博睿宏远

没错,用三方 Github 做授权登录就是这么简单!(OAuth2.0实战)

程序员小富

Java GitHub oauth2.0

第6周课后练习-请简述CAP原理

Dawn

极客大学架构师训练营

2020-07-11-第六周作业

路易斯李李李

路过,凌晨2点的南京

小天同学

总结 思考 个人感悟 夜归人

java 后端博客系统文章系统——No5

猿灯塔

Java

Rust所有权,可转可借

袁承兴

rust 指针 函数调用 引用 内存管理

架构师训练营第 6 周作业二

不谈

HashMap学习总结

大刘

hashmap hash

你要的《Spring系列源码解读》PDF它来了

z小赵

Java spring

分布式系统架构作业

qihuajun

面向对象编程学习

一叶知秋

React与前端开发发展史

pingan8787

指数 | 2020年6月北京BGP机房网络质量评测报告

博睿数据

评测 博睿宏远 指数

详解区块链应用市场与落地应用现状

CECBC

1. react起始 | 2020年前端再入门系列连载

chaozh

大前端 React

MySQL 连接查询超全详解

X先生

MySQL 数据库

架构师训练营第六周作业

一剑

CAP原理

chenzt

架构师训练营第六周作业

R20114

极客大学架构师训练营

用Roslyn做个JIT的AOP

八苦-瞿昙

技术 随笔杂谈 aop 代理 框架

Spring循环依赖及解决方式

张sir

Java spring 循环依赖

对CAP的理解

朱月俊

架构师训练营第六周总结

一剑

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