Slf4j 包老冲突,每次排查半天,是什么原因?怎么解决?

简介: 一、前言在进行 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);

我们就可以通过这个作为入口,去看看源码的实现。如下图所示,我标注了需要关注的核心代码。

  • (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>
相关实践学习
通过日志服务实现云资源OSS的安全审计
本实验介绍如何通过日志服务实现云资源OSS的安全审计。
相关文章
|
Java API Maven
解决SLF4J和logback报错SLF4J: Failed to load class “org.slf4j.impl.StaticLoggerBinder“.
解决SLF4J和logback报错SLF4J: Failed to load class “org.slf4j.impl.StaticLoggerBinder“.
|
机器学习/深度学习 Web App开发 人工智能
Amurex:开源AI会议助手,提供实时建议、智能摘要、快速回顾关键信息
Amurex是一款开源的AI会议助手,提供实时建议、智能摘要、快速回顾关键信息等功能,帮助用户提升会议效率。本文将详细介绍Amurex的功能、技术原理以及如何运行和使用该工具。
702 18
Amurex:开源AI会议助手,提供实时建议、智能摘要、快速回顾关键信息
|
机器学习/深度学习 边缘计算 5G
室内定位之5G定位
室内定位之5G定位
1118 1
|
Java 前端开发 Spring
技术融合新潮流!Vaadin携手Spring Boot、React、Angular,引领Web开发变革,你准备好了吗?
【8月更文挑战第31天】本文探讨了Vaadin与Spring Boot、React及Angular等主流技术栈的最佳融合实践。Vaadin作为现代Java Web框架,与其他技术栈结合能更好地满足复杂应用需求。文中通过示例代码展示了如何在Spring Boot项目中集成Vaadin,以及如何在Vaadin项目中使用React和Angular组件,充分发挥各技术栈的优势,提升开发效率和用户体验。开发者可根据具体需求选择合适的技术组合。
328 0
|
Swift iOS开发 开发者
iOS应用开发:SwiftUI高级技巧
【7月更文挑战第21天】SwiftUI以其声明式语法、跨平台一致性、强大的动画和状态管理特性,为iOS应用开发带来了革命性的变化。通过掌握SwiftUI的高级技巧,开发者可以构建出更加高效、美观和流畅的应用。希望本文能够帮助你更好地理解和应用SwiftUI,从而在iOS应用开发的道路上走得更远。
|
机器学习/深度学习 并行计算 PyTorch
【从零开始学习深度学习】20. Pytorch中如何让参数与模型在GPU上进行计算
【从零开始学习深度学习】20. Pytorch中如何让参数与模型在GPU上进行计算
|
运维 芯片
主板电源符号揭秘:深入了解VDD、VDDQ、5VSB及其他
本文介绍了计算机主板电源设计中的关键符号,包括VDD(通用数字电路电源)、VDDQ(高稳定度滤波电源)、5VSB和3VSB(待机电源)、VCC3(+3V主要电源)、VDIMM(内存专用电源)、SB(待机电池电源)以及VCORE(CPU核心电压)。这些电源符号各自对应特定的供电区域和功能,确保主板组件的稳定运行。理解这些电源符号对于主板电源管理、故障排查和系统优化具有重要意义。
|
Shell
etcd.service: main process exited, code=exited, status=203/EXEC
etcd.service: main process exited, code=exited, status=203/EXEC
631 1
|
消息中间件 存储 Kafka
【Kafka】Kafka 的日志保留期与数据清理策略
【4月更文挑战第13天】【Kafka】Kafka 的日志保留期与数据清理策略
|
弹性计算 负载均衡 算法
SLB配置与使用
SLB配置与使用
2775 4

热门文章

最新文章