上一篇:深夜看了张一鸣的微博,让我越想越后怕

作者:空无
链接:https://juejin.cn/post/6945220055399399455

前言

你是否遇到过配置了日志,但打印不出来的情况?

你是否遇到过配置了logback,启动时却提示log4j错误的情况?

像下面这样:

log4j:WARN No appenders could be found for logger (org.example.App).
log4j:WARN Please initialize the log4j system properly.
log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.

你是否遇到过SLF4J的这种报错?

SLF4J: Class path contains multiple SLF4J bindings.
SLF4J: Found binding in [jar:file:/C:/Users/jiang/.m2/repository/ch/qos/logback/logback-classic/1.2.3/logback-classic-1.2.3.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:/C:/Users/jiang/.m2/repository/org/slf4j/slf4j-log4j12/1.7.30/slf4j-log4j12-1.7.30.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation.
SLF4J: Actual binding is of type [ch.qos.logback.classic.util.ContextSelectorStaticBinder]

你是否遇到过DUBBO日志打印不正常的情况?

你是否遇到过Mybatis SQL日志打印不出来的情况?

你是否遇到过JPA/Hibernate SQL日志无法打印的情况?

你是否遇到过复杂项目中,很多框架内部日志无法打印的情况?

你是否遇到过Tomcat工程,日志文件打印了多份,catalina.out和其他文件?

你是否遇到过Spring Boot项目,日志文件打印了多份的问题?

你是否遇到过各种日志配置问题……


日志框架的冲突

上面的这些问题,基本都是由于多套日志框架共存或配置错误导致的。

那么为什么会出现共存或者冲突呢?

一般是以下几种原因:

  1. 项目手动引用了各种日志框架的包 - 比如同时引用了log4j/log4j2/logback/jboss-logging/jcl等

  2. 包管理工具的传递依赖(Transitive Dependencies)导致,比如依赖了dubbo,但是dubbo依赖了zkclient,可zkclient又依赖了log4j,此时如果你的项目中还有其他日志框架存在并有使用,那么就会导致多套共存

  3. 同一个日志框架多版本共存

JAVA里的各种日志框架

在正式介绍冲突和解决之前,需要先简单的说一下Java中的各种日志框架:

Java 中的日志框架分为两种,分别为日志抽象/门面,日志实现。

日志抽象/门面

日志抽象/门面,他们不负责具体的日志打印,如输出到文件、配置日志内容格式等。他们只是一套日志抽象,定义了一套统一的日志打印标准,如Logger对象,Level对象。

slf4j(Simple Logging Facade for Java)和jcl(Apache Commons Logging)这两个日志框架就是JAVA中最主流的日志抽象了。还有一个jboss-logging,主要用于jboss系列软件,比如hibernate之类。像 jcl已经多年不更新了(上一次更新时间还是14年),目前最推荐的是使用 slf4j。

日志实现

Java 中的日志实现框架,主流的有以下几种:

  1. log4j - Apache(老牌日志框架,不过多年不更新了,新版本为log4j2)

  2. log4j2 - Apache(log4j 的新版本,目前异步IO性能最强,配置也较简单)

  3. logback - QOS(slf4j就是这家公司的产品)

  4. jul(java.util.logging) - jdk内置

在程序中,可以直接使用日志框架,也可以使用日志抽象+日志实现搭配的方案。不过一般都是用日志抽象+日志实现,这样更灵活,适配起来更简单。

目前最主流的方案是slf4j+logback/log4j2,不过如果是jboss系列的产品,可能用的更多的还是jboss-logging,毕竟亲儿子嘛。像JPA/Hibernate这种框架里,内置的就是jboss-logging。

SpringBoot + Dubbo 日志框架冲突的例子

举个例子来说个最常见的传递依赖导致的共存冲突

比如我有一个“干净的”spring-boot项目,干净到只有一个spring-boot-starter依赖,此时我想集成dubbo,使用zookeeper作为注册中心,此时我的依赖配置是这样:

<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId></dependency><dependency><groupId>org.apache.dubbo</groupId><artifactId>dubbo-spring-boot-starter</artifactId><version>2.7.9</version></dependency><dependency><groupId>org.apache.dubbo</groupId><artifactId>dubbo-registry-zookeeper</artifactId><version>2.7.9</version></dependency>
</dependencies>

现在启动这个spring-boot项目,会发现一堆红色错误:

SLF4J: Class path contains multiple SLF4J bindings.
SLF4J: Found binding in [jar:file:/C:/Users/jiang/.m2/repository/ch/qos/logback/logback-classic/1.2.3/logback-classic-1.2.3.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:/C:/Users/jiang/.m2/repository/org/slf4j/slf4j-log4j12/1.7.30/slf4j-log4j12-1.7.30.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation.
SLF4J: Actual binding is of type [ch.qos.logback.classic.util.ContextSelectorStaticBinder]
----------------------------------人肉分割线----------------------------------------
log4j:WARN No appenders could be found for logger (org.apache.dubbo.common.logger.LoggerFactory).
log4j:WARN Please initialize the log4j system properly.
log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.

从错误提示上看,错误内容分为两个部分:

  1. slf4j报错,提示找到多个slf4j的日志绑定

  2. log4j报错,提示log4j没有appender配置

出现这个错误,就是因为dubbo的传递依赖中含有log4j,但是spring-boot的默认配置是slf4j+logback。在依赖了dubbo相关包之后,现在项目中同时存在logback/jcl(apache commons-logging)/log4j/jul-to-slf4j/slf4j-log4j/log4j-to-slf4j

来看一下依赖图:

这个时候就乱套了,slf4j-log4j是log4j的slf4j实现,作用是调用slf4j api的时候使用log4j输出;而log4j-to-slf4j的作用是将log4j的实现替换为log4j,这样一来不是死循环了

而且还有logback的存在,logback默认实现了slf4j的抽象,而slf4j-log4j也是一样实现了slf4j的抽象,logback,项目里共存了两套slf4j的实现,那么在使用slf4j接口打印的时候会使用哪个实现呢?

答案是“第一个”,也就是第一个被加载的Slf4j的实现类,但这种依靠ClassLoader加载顺序来保证的日志配置顺序是非常不靠谱的

如果想正常使用日志,让这个项目里所有的框架都正常打印日志,必须将日志框架统一。不过这里的统一并不是至强行修改,而是用“适配/中转”的方式。

现在项目里虽然有slf4j-log4j的配置,但这个配置是适配log4j2用的,而我们的依赖了只有log4j1,实际上这个中转是无效的。但logback是有效的,而且是spring-boot项目的默认配置,这次就选择logback作为项目的统一日志框架吧。

现在项目里存在log4j(1)的包,而且启动时又报log4j的错误,说明某些代码调用了log4j的api。但我们又不想用log4j,所以需要先解决log4j的问题。

由于有log4j代码的引用,所以直接删除log4j一定是不可行的。slf4j提供了一个log4j-over-slf4j的包,这个包复制了一份log4j1的接口类(Logger等),同时将实现类修改为slf4j了。

所以将log4j的(传递)依赖排除,同时引用log4j-over-slf4j,就解决了这个log4j的问题。

现在来修改下pom中的依赖(查看依赖图可以使用maven的命令,或者是IDEA自带的Maven Dependencies Diagram,再或者Maven Helper之类的插件)关注公众号互联网架构师,在后台回复:2T,可以获取我整理的教程,非常齐全。

<dependency><groupId>org.apache.dubbo</groupId><artifactId>dubbo-registry-zookeeper</artifactId><version>2.7.9</version><scope>compile</scope><!--排除log4j--><exclusions><exclusion><artifactId>log4j</artifactId><groupId>log4j</groupId></exclusion></exclusions>
</dependency>
<!--增加log4j-slf4j -->
<dependency><groupId>org.slf4j</groupId><artifactId>log4j-over-slf4j</artifactId><version>1.7.30</version>
</dependency>

解决了log4j的问题之后,现在还有slf4j有两个实现的问题,这个问题处理就更简单了。由于我们计划使用logback,那么只需要排除/删除slf4j-log4j这个实现的依赖即可

<dependency><groupId>org.apache.dubbo</groupId><artifactId>dubbo-registry-zookeeper</artifactId><version>2.7.9</version><scope>compile</scope><exclusions><exclusion><artifactId>log4j</artifactId><groupId>log4j</groupId></exclusion><exclusion><artifactId>slf4j-log4j12</artifactId><groupId>org.slf4j</groupId></exclusion></exclusions>
</dependency>

修改完成,再次启动就没有错误了,轻松解决问题。

日志适配大全

上面只是介绍了一种转换的方式,但这么多日志框架,他们之间是可以互相转换的。不过最终目的都是统一一套日志框架,让最终的日志实现只有一套 这么多的日志适配/转换方式,全记住肯定是有点难。

为此我画了一张可能是全网最全的日志框架适配图(原图尺寸较大,请点击放大查看),如果再遇到冲突,需要将一个日志框架转换到另一款的时候,只需要按照图上的路径,引入相关的依赖包即可。

比如想把slf4j,适配/转换到log4j2。按照图上的路径,只需要引用log4j-slf4j-impl即可。

如果想把jcl,适配/转换到slf4j,只需要删除jcl包,然后引用jcl-over-slf4j即可。

图上的箭头,有些标了文字的,是需要额外包进行转换的,有些没有标文字的,是内置了适配的实现。其实内置实现的这种会更麻烦,因为如果遇到共存基本都需要通过配置环境变量/配置额外属性的方式来指定一款日志实现。

目前slf4j是适配方案中,最核心的那个框架,算是这个图的中心枢纽。只要围绕slf4j做适配/转化,就没有处理不了的冲突。

总结

解决日志框架共存/冲突问题其实很简单,只要遵循几个原则:

  1. 统一使用一套日志实现

  2. 删除多余的无用日志依赖

  3. 如果有引用必须共存的话,那么就移除原始包,使用“over”类型的包(over类型的包复制了一份原始接口,重新实现)

  4. 不能over的,使用日志抽象提供的指定方式,例如jboss-logging中,可以通过org.jboss.logging.provider环境变量指定一个具体的日志框架实现

项目里统一了日志框架之后,无论用那种日志框架打印,最终还是走向我们中转/适配后的唯一一个日志框架。

解决了共存/冲突之后,项目里就只剩一款日志框架。再也不会出现“日志打不出”,“日志配置不生效”之类的各种恶心问题,下班都能早点了!

感谢您的阅读,也欢迎您发表关于这篇文章的任何建议,关注我,技术不迷茫!小编到你上高速。

· END ·

最后,关注公众号互联网架构师,在后台回复:2T,可以获取我整理的 Java 系列面试题和答案,非常齐全。

正文结束

推荐阅读 ↓↓↓

1.不认命,从10年流水线工人,到谷歌上班的程序媛,一位湖南妹子的励志故事

2.如何才能成为优秀的架构师?

3.从零开始搭建创业公司后台技术栈

4.程序员一般可以从什么平台接私活?

5.37岁程序员被裁,120天没找到工作,无奈去小公司,结果懵了...

6.IntelliJ IDEA 2019.3 首个最新访问版本发布,新特性抢先看

7.漫画:程序员相亲图鉴,笑屎我了~

8.15张图看懂瞎忙和高效的区别!

一个人学习、工作很迷茫?

点击「阅读原文」加入我们的小圈子!

全网最全 Java 日志框架适配方案!还有谁不会?相关推荐

  1. 可能是全网最全,JAVA日志框架适配/冲突解决方案,可以早点下班了

    点击关注公众号,Java干货及时送达 你是否遇到过配置了日志,但打印不出来的情况? 你是否遇到过配置了logback,启动时却提示log4j错误的情况?像下面这样: log4j:WARN No app ...

  2. 可能是全网最全的 Java 日志框架适配、冲突解决方案

    作者:空无 juejin.cn/post/6945220055399399455 前言 你是否遇到过配置了日志,但打印不出来的情况? 你是否遇到过配置了logback,启动时却提示log4j错误的情况 ...

  3. 可能是全网最全,JAVA日志框架适配、冲突解决方案,可以早点下班了!

    点击上方"Java基基",选择"设为星标" 做积极的人,而不是积极废人! 每天 14:00 更新文章,每天掉亿点点头发... 源码精品专栏 原创 | Java ...

  4. Java 日志框架适配/冲突解决方案(值得收藏)

    欢迎关注方志朋的博客,回复"666"获面试宝典 来源:juejin.cn/post/6945220055399399455 前言 你是否遇到过配置了日志,但打印不出来的情况? 你是 ...

  5. Java 日志框架适配/冲突解决方案

    前言 你是否遇到过配置了日志,但打印不出来的情况? 你是否遇到过配置了logback,启动时却提示log4j错误的情况?像下面这样: log4j:WARN No appenders could be ...

  6. 最牛逼的 Java 日志框架

    Logback 算是JAVA 里一个老牌的日志框架,从06年开始第一个版本,迭代至今也十几年了.不过logback最近一个稳定版本还停留在 2017 年,好几年都没有更新:logback的兄弟 slf ...

  7. 最牛逼的 Java 日志框架,性能无敌,横扫所有对手

    Logback 算是JAVA 里一个老牌的日志框架,从06年开始第一个版本,迭代至今也十几年了.不过logback最近一个稳定版本还停留在 2017 年,好几年都没有更新:logback的兄弟 slf ...

  8. 最牛逼的 Java 日志框架,性能无敌,横扫所有对手。。

    点击关注公众号,Java干货及时送达 作者:空无 链接:https://juejin.cn/post/6945753017878577165 Logback 算是JAVA 里一个老牌的日志框架,从06 ...

  9. 获取日志的等级_进阶之路:Java 日志框架全画传(中)

    导读:随着互联网和大数据的蓬勃发展,分布式日志系统以及日志分析系统得到了广泛地应用.目前,几乎在所有应用程序中,都会用到各种各样的日志框架来记录程序的运行信息.鉴于此,工程师十分有必要熟悉主流的日志记 ...

  10. java log4j logback jcl_进阶之路:Java 日志框架全画传(下)

    导读:随着互联网和大数据的蓬勃发展,分布式日志系统以及日志分析系统得到了广泛地应用.目前,几乎在所有应用程序中,都会用到各种各样的日志框架来记录程序的运行信息.鉴于此,工程师十分有必要熟悉主流的日志记 ...

最新文章

  1. 简单介绍python连接telnet和ssh的两种方式
  2. 如何写出一份优秀的软件设计文档
  3. 美团点评基于 Flink 的实时数仓建设实践
  4. 2021年4月12日 阿里供应链Java研发实习面试(一面)(含总结)
  5. 星巴克和阿里“结婚”,这后面真的不简单
  6. 上云有风险 公有云选型小心进坑
  7. zabbix php 5.6 安装配置,CentOS 5.6下Zabbix 1.8.5 服务端安装部署
  8. NOTEBOOK随笔
  9. pacman吃豆人_通过Tensorflow和强化学习实现自动化吃豆人PacMan
  10. linux 系统下如何进行用户之间的切换
  11. linux下为.net core应用创建守护进程
  12. SAP License:PS-七日通-第二通-预算管理
  13. 为英文的windows 7打上简体中文语言包
  14. QCC3005 控制AMP_Mute的管脚配置问题
  15. Python:运行时隐藏窗口
  16. 中学生怎样才能合理使用计算机,浅析中学生计算机的使用
  17. 全面认识当前市面99%的大数据技术框架(附:各大厂大数据技术应用文章)
  18. 2021-11-03皮革制作与环保科技
  19. Roboware安装
  20. 加拿大卫生部药品信息数据查询

热门文章

  1. Centos 7忘记密码,如何重置
  2. DES加密(支持ARC与MRC)
  3. leetcode[541]翻转字符串里的单词/reverse words in a string 综合考察了字符串的多种操作
  4. Pr 2021快速入门教程,素材的导入与管理
  5. 如何在几秒钟内退出所有 Mac 应用程序?
  6. indesign教程,如何使用共享交互式文档?
  7. iOS开发之cocoapods安装(2017)
  8. Mac如何在回收站中恢复丢失数据
  9. 如何在“活动监视器”中检查Mac是否需要更多内存?
  10. Mac无法开机?如何修复