V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
stephCurry
V2EX  ›  Java

SLF4J 冲突, 如何指定其中一个为实现类

  •  
  •   stephCurry · 2019-10-21 22:18:09 +08:00 · 6105 次点击
    这是一个创建于 1850 天前的主题,其中的信息可能已经有所发展或是发生改变。
    SLF4J: Class path contains multiple SLF4J bindings.
    SLF4J: Actual binding is of type [org.slf4j.impl.SimpleLoggerFactory]
    
    Exception in thread "main" java.lang.IllegalArgumentException: LoggerFactory is not a Logback LoggerContext but Logback is on the classpath. Either remove Logback or the competing implementation
    

    from SLF4J multiple_bindings Doc

    NOTE The warning emitted by SLF4J is just that, a warning. Even when multiple bindings are present, SLF4J will pick one logging framework/implementation and bind with it. The way SLF4J picks a binding is determined by the JVM and for all practical purposes should be considered random.

    如上所示, 由于 SLF4J binding 是 random 的,所以这里就随机引用到了 Actual binding is of type [org.slf4j.impl.SimpleLoggerFactory] 导致 IllegalArgumentException

    把此 gradle 项目换成 maven 时,它就随机绑定到了 Actual binding is of type [ch.qos.logback.classic.util.ContextSelectorStaticBinder] , 此时启动没问题, 但是对于 gradle 项目还是想去解决这个问题。

    主要问题还是引的 jar 依赖( soot-infoflow-cmd )里直接把 slf4j 侵入到代码里了,所以无法在这个依赖下 exclude 掉内部的 slf4j。

    解决方式也说了, Either remove Logback or the competing implementation , 要删 logback,可是我不想 exclude springboot-starter-logging, 要么 competing implementation, 那么这里如何去 compete implementation 呢?如何让它强制 bind [ch.qos.logback.classic.util.ContextSelectorStaticBinder] 呢?

    18 条回复    2019-11-03 17:53:31 +08:00
    Samuelcc
        1
    Samuelcc  
       2019-10-22 00:32:38 +08:00 via Android
    slf4j 和 它的实现的依赖应该是分开的吧,代码里如果直接用的 slf4j 的接口,应该 exclude 掉对应实现就行,不用 exclude slf4j。
    NeinChn
        2
    NeinChn  
       2019-10-22 01:00:14 +08:00
    一般 exclude 可能因为 level 太深依赖关系太复杂 exclude 比较麻烦
    通用做法是加个空白的实现 deploy 到 maven 仓库,然后在 top level 显式声明这个依赖
    比如 slf4j-simple,自己写个
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-simple</artifactId>
    <version>empty-version</version>
    airfling
        3
    airfling  
       2019-10-22 08:37:11 +08:00
    我把 slf4j 的全去掉,只用 logback,用着没啥问题
    dif
        4
    dif  
       2019-10-22 09:29:58 +08:00
    @airfling 这种情况仅适合自己用。最好还是用 slf4j 吧,切换日志实现都方便。
    qwerthhusn
        5
    qwerthhusn  
       2019-10-22 09:33:53 +08:00
    org.slf4j.LoggerFactory#bind
    这个方法,看了一圈没有能找到控制其实现流程的地方。

    如果想丑陋的且非常简单的改变,在你的 src/main/java 下面建一个 org.slf4j.LoggerFactory,把原有的代码拷过来,然后修改相关的逻辑,程序运行的时候就会加载到动了手脚的这个
    stephCurry
        6
    stephCurry  
    OP
       2019-10-22 10:02:31 +08:00
    @airfling 我也想去, 但是去不掉啊
    airfling
        7
    airfling  
       2019-10-22 10:15:24 +08:00
    exclude group: 'org.slf4j', module: 'slf4j-log4j12'
    exclude group: 'log4j', module: 'log4j'
    这两个你加上试试
    git00ll
        8
    git00ll  
       2019-10-22 11:40:51 +08:00
    #### 将你想使用的实现类配置在第一个,下面 slf4j-simple 配置都在 logback-classic 前面,使用的就是 slf4j-simple

    ```

    dependencies {
    testCompile group: 'junit', name: 'junit', version: '4.12'
    compile group: 'org.slf4j', name: 'slf4j-api', version: '1.7.28'
    compile group: 'org.slf4j', name: 'slf4j-simple', version: '1.7.28'
    compile group: 'ch.qos.logback', name: 'logback-classic', version: '1.2.3'
    }



    public static void main(String[] args) throws IOException {
    Logger log = LoggerFactory.getLogger(Test.class);
    log.info("info");
    }

    ```
    Jrue0011
        9
    Jrue0011  
       2019-10-22 13:42:14 +08:00
    看了下 github 上你说的这个项目,POM 里直接引用的是 slf4j-simple,但是代码里只使用了 slf4j-api 的 LoggerFactory 和 Logger,所以直接排除 slf4j-simple 就行了
    stephCurry
        10
    stephCurry  
    OP
       2019-10-22 15:57:05 +08:00
    @Jrue0011 不行,exclude 不掉,soot-infoflow-cmd-jar-with-dependencies.jar code 里已经硬引用了 org/slf4j
    Jrue0011
        11
    Jrue0011  
       2019-10-22 17:18:46 +08:00   ❤️ 1
    @stephCurry 是不是直接下载的 soot-infoflow-cmd-jar-with-dependencies.jar 这个 jar 包然后引用到项目里?是的话可以直接打开压缩包删掉 org/slf4j/impl 目录。

    但既然用 gradle 和 maven 管理依赖了,好点的做法是下载他的项目源码,maven 打包安装到本地仓库,然后就可以在自己项目的 pom.xml 或 build.gradle 里引用了,但是打包的时候由于环境问题可能出错。。。

    https://github.com/secure-software-engineering/FlowDroid
    参考 README.md 的 Building The Tool With Maven
    lxk11153
        12
    lxk11153  
       2019-10-29 14:11:07 +08:00
    既然是`硬包含`了 SimpleLoggerFactory,exclude 不掉,我列举下能想到的几种做法:
    1. 使用 soot-infoflow-cmd.jar 不要用 with-dependencies.jar
    2. 可调整 maven 仓库吗?下载 with-dependencies.jar ,删了不想要的类,发布上去
    3. `considered random` 视为随机,并没有说它是真的随机,根据 java 的加载,好像是看引入顺序的,一般没调整的话就是按照 jar 文件名顺序载入类。根据这就可以用“从文件加载类”+反射机器 自行调整具体的 log factory
    lxk11153
        13
    lxk11153  
       2019-10-29 14:13:33 +08:00
    3. `considered random` 视为随机,并没有说它是真的随机,根据 java 的加载,是看引入顺序的,一般没调整的话就是按照 jar 文件名顺序载入类。根据这就可以用“从文件加载类”(可能加上反射机制) 自行调整到你要的 log factory
    PoetAndPoem
        14
    PoetAndPoem  
       2019-10-29 14:39:11 +08:00
    stephCurry
        15
    stephCurry  
    OP
       2019-11-03 10:57:06 +08:00
    @lxk11153 把 jar 文件名改成 a 开头/ z 开头,还是不行。。。。 自己写的` StaticLoggerBinder implements LoggerFactoryBinder ` 不知怎么也没扫到, 只能尝试去 rebuild FlowDroid 了
    lxk11153
        16
    lxk11153  
       2019-11-03 14:23:17 +08:00   ❤️ 1
    @stephCurry #15
    private static String STATIC_LOGGER_BINDER_PATH = "org/slf4j/impl/StaticLoggerBinder.class";

    你可以把你 java 的 command 发出来,我来看看
    linux: pgrep -af "java" macos: pgrep -lf "java" 至于 Windows 可以用 docs.microsoft.com/zh-cn/sysinternals/downloads/process-explorer 右键列标题把 command 显示出来就能看到
    stephCurry
        17
    stephCurry  
    OP
       2019-11-03 15:30:49 +08:00
    @lxk11153 谢谢! 方便的话 可否 atob d2VjaGF0OiAxMzA1MTE1MjY1MA==
    lxk11153
        18
    lxk11153  
       2019-11-03 17:53:31 +08:00
    @lxk11153 #16 至于 Windows 好像 任务管理器 右键列标题把 command 显示出来就能看到,至于能否复制出来不清楚,自己查找解决办法
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   5858 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 32ms · UTC 02:43 · PVG 10:43 · LAX 18:43 · JFK 21:43
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.