sfl4j日志框架的加载导致冲突问题的原因和解决方式

本文深入分析了SLF4J日志框架加载冲突的问题,主要探讨了两种常见情况:未引入实现包导致的日志打印失效,以及引入多个实现包引起的多重绑定警告。文章提供了详细的解决方案,包括如何检查和修正依赖,以及源码级的解析。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

日志框架的加载导致冲突问题的原因

  经常使用slf4j-api接口包时引入log4j包,总是出现下列的错误,网上查找了一圈,都是说实现包冲突,几乎都是官网的答案。比较少有分析为什么会报这个错的原因。主要有两大类问题:1.引入了slf4j-api的包,但没有引入它的实现包,2.引入了slf4j-api的包,但是实现类引入了多个。

未引入sfl4j-api包的实现包

  报错Failed to load class "org.slf4j.impl.StaticLoggerBinder",使用NOP实现,其实就是空调用,打印不出日志。

SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.

解决的方法也比较简单:

  引入slf4j-api的实现,slf4j的实现包主要有slf4j-simple,slf4j-log4j12,对于这两个实现包,可以在这个网站搜索:https://mvnrepository.com/,如果是maven项目则很方便配置,如果不是则选择下载的链接下载下来。这两个实现包引入其中一个即可。

引入多个sfl4j-api包的实现包

  如果引入了多个slf4j-api的实现包,则会报错:SLF4J: Class path contains multiple SLF4J bindings.,但一般slf4j会自行取其中一个实现进行打印。但最好保证只有一个实现包。

SLF4J: Class path contains multiple SLF4J bindings.
SLF4J: Found binding in [jar:file:/D:/Maven/repository/org/slf4j/slf4j-log4j12/1.7.27/slf4j-log4j12-1.7.27.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:/D:/Maven/repository/org/slf4j/slf4j-simple/1.7.28/slf4j-simple-1.7.28.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 [org.slf4j.impl.Log4jLoggerFactory]

  有时候我们的项目引入的包很复杂,有些日志包是传递以来引入的,导致很难找到具体的引入,如果是maven项目那么可以使用mvn dependency:tree > tree.txt将项目的依赖导入到文件中,然后我们可以搜索slf4j或者log字样,找到引入的日志包,进而判断是否重复了,是否冲突了。如下使用mvn dependency:tree
在这里插入图片描述
  有了这个依赖树,很方便就可以找到那个日志依赖引入冲突了,从而可以将多引入的日志实现去掉。

slf4j-api查找依赖源码分析

  直接看org.slf4j.LoggerFactory#getLogger(java.lang.Class<?>)的实现,LoggerFactoryslf4j-api包中的一个类,它也是slf4j日志的入口,通过getLogger(Class<?> clazz)获取Logger实例,打印日志。

 public static Logger getLogger(Class<?> clazz) {
        //<1>获取日志实例
        Logger logger = getLogger(clazz.getName());
        //是否检测日志的名字不匹配,其实是clazz类是否在这个打印日志的地方的堆栈中存在的,默认false
        if (DETECT_LOGGER_NAME_MISMATCH) {
            //<2> 获取调用堆栈的类,并且去除slf-api中的类。保证打印的是应用的堆栈类的信息。
            Class<?> autoComputedCallingClass = Util.getCallingClass();
            //<3>如果clazz不在堆栈中则打印提示信息
            if (nonMatchingClasses(clazz, autoComputedCallingClass)) {
                Util.report(String.format("Detected logger name mismatch. Given name: \"%s\"; computed name: \"%s\".", logger.getName(), autoComputedCallingClass.getName()));
                Util.report("See http://www.slf4j.org/codes.html#loggerNameMismatch for an explanation");
            }
        }

        return logger;
    }

  getLogger方法,会调用org.slf4j.LoggerFactory#getILoggerFactory

 public static ILoggerFactory getILoggerFactory() {
        //<1> 判断是否初始化过,如果已经初始化过Logger则INITTIALZATION_STATE为0
        if (INITIALIZATION_STATE == 0) {
            INITIALIZATION_STATE = 1;
            //<2> 否则进行初始化,核心!!!
            performInitialization();
        }

        switch(INITIALIZATION_STATE) {
        case 1:
            //初始化过了,返回已经初始化factory
            return TEMP_FACTORY;
        case 2:
            //初始化失败
            throw new IllegalStateException("org.slf4j.LoggerFactory could not be successfully initialized. See also http://www.slf4j.org/codes.html#unsuccessfulInit");
        case 3:
            //单例的日志工厂实例
            return StaticLoggerBinder.getSingleton().getLoggerFactory();
        case 4:
            //没有找到实现类,返回空实现
            return NOP_FALLBACK_FACTORY;
        default:
            //其他异常
            throw new IllegalStateException("Unreachable code");
        }
    }

  performInitialization()往里面走最终会走到bind()方法,这里决定了INITIALIZATION_STATE,代码如下所示:

  private static final void bind() {
        String msg;
        try {
            //<1> 查找"org/slf4j/impl/StaticLoggerBinder.class"的实现类,可能存在多个所以是Set
            Set<URL> staticLoggerBinderPathSet = findPossibleStaticLoggerBinderPathSet();
            //<2> 如果有多个,则提示有多个实现。
            reportMultipleBindingAmbiguity(staticLoggerBinderPathSet);
            //<3> 获取实际的实现StaticLoggerBinder,这个其实是在实现包中的,如果没有引入实现,是会抛异常的
            StaticLoggerBinder.getSingleton();
            INITIALIZATION_STATE = 3;//初始化状态为3,单例
            //<4> 打印具体的实现
            reportActualBinding(staticLoggerBinderPathSet);
            fixSubstitutedLoggers();
        } catch (NoClassDefFoundError var2) {
            msg = var2.getMessage();
            if (!messageContainsOrgSlf4jImplStaticLoggerBinder(msg)) {
                //没找到"org/slf4j/impl/StaticLoggerBinder.class"实现类
                failedBinding(var2);
                throw var2;
            }

            INITIALIZATION_STATE = 4;
            //<5>未加载到实现类,对应了第一种情况,可能没有引入实现包。
            Util.report("Failed to load class \"org.slf4j.impl.StaticLoggerBinder\".");
            Util.report("Defaulting to no-operation (NOP) logger implementation");
            Util.report("See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.");
        } catch (NoSuchMethodError var3) {
            msg = var3.getMessage();
            if (msg != null && msg.indexOf("org.slf4j.impl.StaticLoggerBinder.getSingleton()") != -1) {
                INITIALIZATION_STATE = 2;
                Util.report("slf4j-api 1.6.x (or later) is incompatible with this binding.");
                Util.report("Your binding is version 1.5.5 or earlier.");
                Util.report("Upgrade your binding to version 1.6.x.");
            }

            throw var3;
        } catch (Exception var4) {
            failedBinding(var4);
            throw new IllegalStateException("Unexpected initialization failure", var4);
        }

 }
  • 查找"org/slf4j/impl/StaticLoggerBinder.class"的实现类
  • 如果有多个,则提示有多个实现。
  • 获取实际的实现StaticLoggerBinder,这个其实是在实现包中的,如果没有引入实现,是会抛异常的。
  • 异常情况的处理和判断,更新INITIALIZATION_STATE。

  reportMultipleBindingAmbiguity(Set<URL> staticLoggerBinderPathSet)里主要是提示有多个实现类,并且选择第一个实现类作为实现。

private static void reportMultipleBindingAmbiguity(Set<URL> staticLoggerBinderPathSet) {
        if (isAmbiguousStaticLoggerBinderPathSet(staticLoggerBinderPathSet)) {
            Util.report("Class path contains multiple SLF4J bindings.");
            Iterator iterator = staticLoggerBinderPathSet.iterator();

            while(iterator.hasNext()) {
                URL path = (URL)iterator.next();
                Util.report("Found binding in [" + path + "]");
            }

            Util.report("See http://www.slf4j.org/codes.html#multiple_bindings for an explanation.");
        }

    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值