Slf4j 包老冲突, 到底怎么解决?

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介: 在进行 Java 开发时,通常我们会选择 Slf4j 作为日志门面,但日志实现却不尽相同。如果系统运行中同时存在多个日志实现,就会出现类似下图的 Warning。

一、前言

在进行 Java 开发时,通常我们会选择 Slf4j 作为日志门面,但日志实现却不尽相同。如果系统运行中同时存在多个日志实现,就会出现类似下图的 Warning。

image.png

二、问题原因

我们知道 SpringBoot 默认使用的日志实现是 Logback,因此我们尝试在项目中引入 Log4j 的依赖时,就复现了上图的报错。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>

上图报错告知我们存在多个 SLF4J bingdings,分别位于 logback 和 log4j 包中,有两个 StaticLoggerBinder。

我们知道使用 Slf4j ,需要 LoggerFactory.getLogger() 方法获取实例。

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

private final Logger logs = LoggerFactory.getLogger(xxx.class); 我们就可以通过这个作为入口,去看看源码的实现。如下图所示,我标注了需要关注的核心代码。image.png

(1)调用 getILoggerFactory() 方法得到 LoggerFactory。


(2)对于首次调用,INITIALIZATION_STATE 应该是 UNINITIALIZED,所以进入初始化的逻辑,调用方法 performInitialization()。


(3)调用 bind() 方法。


(4)如果不是 isAndroid(),调用


findPossibleStaticLoggerBinderPathSet() 方法,故名思意,查找可能的 staticLoggerBinder,注意这里返回的类型是 SET,即可能是多个。


(5)在findPossibleStaticLoggerBinderPathSet() 这个方法内,首先通过 classLoader 加载了 org/slf4j/impl/StaticLoggerBinder.class 这个类的 path,它可能存在多个,因此使用了 while 获取了所有的 path,并最终返回。

image.png

(6)reportActualBinding() 方法会校验 SET 的 size,如果大于 1,就会打印出一开始我们看见的 Warning 了。image.png

三、问题解决

解决思路就是将你不想要的日志实现从依赖包中排除掉即可,通过 IDEA 提供的 Diagrams 能够非常方便的查看项目中的依赖关系。


打开项目的 POM 文件,右键选择 Diagrams -> Show Dependencies


image.png


假设我们想要排除 logback 依赖,使用 log4j。Ctrl + F 搜索 logback,可以找到引用该依赖的树形结构。


image.png


点击窗口左上角的下图中的这个图标,可以只看当前选中的这个依赖的关系。


image.png


选中后效果如下:


image.png


如上图所示,logback 由 spring-boot-starter-logging 引入,最顶层是由 spring-boot-starter-web 和 spring-boot-starter-test 引入。


我们尝试在 spring-boot-starter-web 中排除该依赖,应该就可以了。如果排出后重新搜索仍然存在 logback 依赖,则重复执行排除的操作。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <exclusions>
        <exclusion>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-logging</artifactId>
        </exclusion>
    </exclusions>
</dependency>

四、总结

日志框架冲突特别对于新手来说处理起来比较头疼,因为涉及到了日志接口和日志实现。

我们推崇的应该是面向接口编程,因此我们大到开源项目,小到公司的公共 jar 包,应当合理利用 Maven 的传递机制。具体的日志实现不应该传递出去,避免影响到调用的下游方。

<optional>true</optional>
相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
相关文章
|
5月前
|
IDE Java Maven
排查maven 冲突及解决方式
【9月更文挑战第25天】在开发过程中,Maven 冲突可能导致多种问题。本文介绍排查方法:查看错误日志中的提示信息;使用 `mvn dependency:tree` 命令检查依赖树;利用 IDE 工具进行依赖分析。解决冲突的方式包括:排除依赖、手动指定版本、更新依赖以及使用 Maven 插件如 Maven Enforcer Plugin 强制依赖一致性。处理时需确保项目稳定与兼容。
528 2
|
分布式计算 Hadoop Java
HBase 安装之后版本的验证的bug:(错误的替换、找不到或无法加载主类、SLF4J)
HBase 安装之后版本的验证的bug:(错误的替换、找不到或无法加载主类、SLF4J)
1059 1
HBase 安装之后版本的验证的bug:(错误的替换、找不到或无法加载主类、SLF4J)
|
6月前
|
Java Apache
java依赖冲突解决问题之NoClassDefFoundError异常如何解决
java依赖冲突解决问题之NoClassDefFoundError异常如何解决
|
6月前
|
Java 中间件 测试技术
java依赖冲突解决问题之jar包版本冲突无法通过升降级解决时如何解决
java依赖冲突解决问题之jar包版本冲突无法通过升降级解决时如何解决
|
6月前
|
Java Maven 容器
java依赖冲突解决问题之ClassNotFoundException定位确认异常如何解决
java依赖冲突解决问题之ClassNotFoundException定位确认异常如何解决
|
5月前
|
Java Maven 开发工具
解决依赖冲突中NoSuchMethodError错误的方法探索
解决 `NoSuchMethodError`错误是一个需要精细操作的过程,它要求开发者不仅要有扎实的编程基础,还需要对项目依赖关系有深刻的理解。通过以上探讨的策略,希望能帮助你有效地解决这一问题。
168 14
|
6月前
|
存储 弹性计算 Java
java依赖冲突解决问题之应用依赖过于复杂如何解决
java依赖冲突解决问题之应用依赖过于复杂如何解决
|
6月前
|
Java 测试技术 Maven
java依赖冲突解决问题之多个版本的jar包中都没有找到缺失的方法如何解决
java依赖冲突解决问题之多个版本的jar包中都没有找到缺失的方法如何解决
|
9月前
|
SQL Oracle 关系型数据库
Seata常见问题之2.0报错找不到类如何解决
Seata 是一个开源的分布式事务解决方案,旨在提供高效且简单的事务协调机制,以解决微服务架构下跨服务调用(分布式场景)的一致性问题。以下是Seata常见问题的一个合集
274 0
|
存储 缓存 安全
【Spring源码】循环依赖如何处理?
面试官:“看过Spring源码吧,简单说说Spring如何解决循环依赖问题?” 大神仙:“Spring利用到了三级缓存来解决循环依赖问题”。 面试官:“三级缓存是怎么处理的?为什么一定得是三级缓存?三级缓存别是对应存储的是什么?” 大神仙:“......”
192 0
【Spring源码】循环依赖如何处理?