作者:Sharehub前言Java 有很多的日志框架可以选择 , 当同一个项目中出现多种日志框架时就很容易出现日志框架冲突的问题 , 导致日志打印不出来 。本文将以一次典型的日志冲突排查问题为例 , 提供排查步骤和思路 , 最后分析日志框架冲突的原因 。
来源:blog.xiaohansong.com/JAVA-log-confict-solve.html
一般行文思路都是先讲 Why , 再讲 How , 这里我颠倒了 , 因为一般遇到问题的时候我们对问题背后的根本原因是一无所知的 , 如果我们已经知道问题的原因 , 那么问题也就迎刃而解了 。
因此我希望先复现我当时在对日志框架了解不多的情况下排查问题的思路和步骤 , 如何在面对未知问题找到破题思路是非常重要的技能 。
一次典型的日志冲突排查问题背景在 A 工程中 , 日志框架配置选用了 Log4j2 , master 分支上日志打印正常 , 但开发分支增加了代码之后日志打印不出来 。项目的依赖中包含了 Log4j2、Logback 等日志框架 。
排查思路与过程排查问题的时候首先必须要有明确的思路 , 即大胆假设 , 小心求证 , 不能像无头苍蝇一样乱试 。从问题的现象看 , 直觉上可以得出几个假设:
- 服务器环境有问题
- 开发分支的 Log4j2 配置有问题
在已有假设都验证失败的情况下 , 需要收集更多的信息作出判断 , 接下来就是要用对照实验收集信息 。于是我分别断点了 A 和 B 两个工程 , 观察它们日志实体的类型是否一致 。结果发现两者的日志实体类型不一样 , A 的日志实现是 Logback , B 的日志实现是 Log4j2 , 很明显 A 打印不出日志是因为日志实体不对 , 但是两者都是用的同一个 LoggerFactory 创建 Logger 的 。
从对照实验的结果来看 , 可以得出一个假设:依赖冲突导致了 A 运行时使用日志实体不是 Log4j2 。
至此我们已经找到了问题的大致方向 , 接下来就是要排包 。排包一般有两种思路:
- 暴力求解:把所有可能冲突的日志包排掉 , 一个个试 。
- 精准爆破:利用类加载的信息判断运行时加载的具体是哪个 jar
获取日志实体的方式如下:
privatestaticfinalLoggerLOGGER=LoggerFactory.getLogger(xxx.class);
LoggerFactory 的代码如下:文章插图
从代码上可以发现 , getLog方法是来自父类LogFactory , 当我去尝试获取LogFactory的实现时候 , 发现竟然有 3 个 jar 中都有同样包名的LogFactory实现 。于是我断点了 A 和 B 工程的代码 , 用 IDEA 的运行代码功能执行以下命令获取LogFactory的加载信息 。
文章插图
结果发现 B 工程使用是spring-jcl , A 使用的是jcl-over-slf4j , 然后排除掉 A 中jcl-over-slf4j , 问题解决 。
上面的排查过程中 , 关键的地方有两点:
- 定位到问题的根源是类加载冲突 , 确定排查方向 。
- 通过断点获取冲突类的加载信息 , 快速定位到冲突的 jar 。
日志框架的历史首先要从日志框架的发展历史开始说起 。
- 首先登场是Java Util Log , 简称JUL , 是JDK 中自带的 log 功能 。虽然是官方自带的 , JUL 的使用却不广泛 , 主要是因为功能比较简单 , 不好用 。
- 然后Log4j 1.x就登场了:它是 Gülcü 设计实现的日志框架 , 设计非常优秀 , 是非常广泛使用的框架 。
推荐阅读
- java日志logback入门
- JavaScript基础语法
- 学会这12个框架,你的薪资和level能更上一层楼
- HTML5开发常见的7个框架,你最常用哪个?
- 跨平台的C语言网络框架库acl
- Java代码中关闭流需要注意的一些细节
- Java 获取属性和环境变量的方法
- MySQL基本命令整理,java数据库秘籍!
- 一篇文章带你吃透,Java界最神秘技术ClassLoader
- JAVA各种锁的优劣对比分析