log4j-over-slf4j与slf4j-log4j12共存stack overflow异常分析

Posted java一日一条

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了log4j-over-slf4j与slf4j-log4j12共存stack overflow异常分析相关的知识,希望对你有一定的参考价值。

注:下文中的“桥接”、“转调”、“绑定”等词基本都是同一个概念。

log4j-over-slf4j和slf4j-log4j12是跟java日志系统相关的两个jar包,当它们同时出现在classpath下时,就可能会引起堆栈溢出异常。异常信息大致如下(摘自slf4j官网文档Detected both log4j-over-slf4j.jar AND slf4j-log4j12.jar on the class path, preempting StackOverflowError ):

现有日志体系

分析这个异常出现的具体原因之前,有必要先快速了解一下现有的Java日志体系。Java日志体系大体可以分为三个部分:日志门面接口、桥接器、日志框架具体实现。

Java日志框架有很多种,最简单的是Java自带的java.util.logging,而最经典的是log4j,后来又出现了一个比log4j性能更好的logback,其他的日志框架就不怎么常用了。应用程序直接使用这些具体日志框架的API来满足日志输出需求当然是可以的,但是由于各个日志框架之间的API通常是不兼容的,这样做就使得应用程序丧失了更换日志框架的灵活性。

比直接使用具体日志框架API更合理的选择是使用日志门面接口。日志门面接口提供了一套独立于具体日志框架实现的API,应用程序通过使用这些独立的API就能够实现与具体日志框架的解耦,这跟JDBC是类似的。最早的日志门面接口是commons-logging,但目前最受欢迎的是slf4j。

日志门面接口本身通常并没有实际的日志输出能力,它底层还是需要去调用具体的日志框架API的,也就是实际上它需要跟具体的日志框架结合使用。由于具体日志框架比较多,而且互相也大都不兼容,日志门面接口要想实现与任意日志框架结合可能需要对应的桥接器,就好像JDBC与各种不同的数据库之间的结合需要对应的JDBC驱动一样。

独立的桥接器有时候是不需要的,而且也并不是只有将日志门面API转调到具体日志框架API的桥接器,也存在将日志框架API转调到日志门面API的桥接器。

说白了,所谓“桥接器”,不过就是对某套API的伪实现。这种实现并不是直接去完成API所声明的功能,而是去调用有类似功能的别的API。这样就完成了从“某套API”到“别的API”的转调。如果同时存在A-to-B.jar和B-to-A.jar这两个桥接器,那么可以想象当应用程序开始调用A或者B的API时,会发生什么事。这就是最开始引出的那个stack overflow异常的基本原理。

slf4j的转接绑定

上面只是从整体上大概说了下Java现有日志体系,还看无法详细说明问题所在,需要进一步了解一下slf4j与具体日志框架的桥接情况。

slf4j与具体日志框架结合的方案有很多种。当然,每种方案的最上层(绿色的应用层)都是统一的,它们向下都是直接调用slf4j提供的API(浅蓝色的抽象API层),依赖slf4j-api.jar。然后slf4j API向下再怎么做就非常自由了,几乎可以使用所有的具体日志框架。注意图中的第二层是浅蓝色的,看左下角的图例可知这代表抽象日志API,也就是说它们不是具体实现。如果像左边第一种方案那样下层没有跟任何具体日志框架实现相结合,那么日志是无法输出来的( 这里不确定是否可能会默认输出到标准输出 )。

代码示例

面的分析都是理论上的,实际代码中即便同时使用了log4j-over-slf4j和slf4j-log4j12,也未必一定会出现异常。下面的代码调用slf4j的API输出日志,slf4j底层桥接到log4j:

log4j-over-slf4j与slf4j-log4j12共存stack overflow异常分析

配置classpath上的jar包为(注意log4j在log4j-over-slf4j之前):

在这种情况下运行测试程序是能够正常输出日志的,不会出现stack overflow异常。但是如果调整classpath上的jar顺序为:

再运行测试程序就出现类似于本文最开始的stack overflow异常了,可以看到明显的周期性重复


以上是关于log4j-over-slf4j与slf4j-log4j12共存stack overflow异常分析的主要内容,如果未能解决你的问题,请参考以下文章

log4j-over-slf4j工作原理详解

使用 maven 和 spring boot 在类路径上检测到 log4j-over-slf4j.jar 和 slf4j-log4j12.jar

Detected both log4j-over-slf4j.jar AND bound slf4j-log4j12.jar on the class path 解决过程

log4j日志实现重复警告slf4j-jdk14和log4j-over-slf4j

开发常见错误之 : Detected both log4j-over-slf4j.jar AND slf4j-log4j12.jar

在类路径上检测到log4j-over-slf4j.jar和slf4j-log4j12.jar,抢占StackOverflowError。