log4j 1.x 升级到 2.x 依赖不兼容问题的解决

Posted 不可描述的两脚兽

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了log4j 1.x 升级到 2.x 依赖不兼容问题的解决相关的知识,希望对你有一定的参考价值。

文章目录

1. 前言

  1. log4j 出现了重大漏洞,黑客可以通过ldap的注入漏洞而篡改后台代码,因而全球的技术部门几乎在同一时间进行着log4j的升级。我司一个老产品用的是log4j 1.2.15的版本,虽然并不受这次漏洞的影响但领导还是要求升级到最新的log4j 2.1.17
  2. 产品架构采用jersey作为web框架,再部署到tomcat作为容器启动
  3. 直接替换jar,移除log4j 1.2.15版本的jarlog4j-1.2.15.jar,增加新版本jar:
    log4j-1.2-api-2.17.1.jar,
    log4j-api-2.17.1.jar,
    log4j-core-2.17.1.jar
    没错,log4j 2.x做了架构上的解耦因此有3个jar,而原来的1.x版本只有一个jar。这次的漏洞出现在core包里面,所以1.x本身不受这次漏洞的影响。
  4. 做完替换工作,尝试启动失败,抛出异常java.lang.ArrayIndexOutOfBoundsException
com.sun.jersey.api.core.ScanningResourceConfig localhost-startStop-1 SEVERE: catch scan exception:  (java.lang.ArrayIndexOutOfBoundsException: 52264)
  java.lang.ArrayIndexOutOfBoundsException: 52264
      at org.objectweb.asm.ClassReader.readClass(Unknown Source)
      at org.objectweb.asm.ClassReader.accept(Unknown Source)
      at org.objectweb.asm.ClassReader.accept(Unknown Source)
      at com.sun.jersey.spi.scanning.AnnotationScannerListener.onProcess(AnnotationScannerListener.java:133)
      at com.sun.jersey.core.spi.scanning.JarFileScanner.scan(JarFileScanner.java:97)
      at com.sun.jersey.spi.scanning.servlet.WebAppResourcesScanner$1.f(WebAppResourcesScanner.java:94)
      at com.sun.jersey.core.util.Closing.f(Closing.java:71)
      at com.sun.jersey.spi.scanning.servlet.WebAppResourcesScanner.scan(WebAppResourcesScanner.java:92)
      at com.sun.jersey.spi.scanning.servlet.WebAppResourcesScanner.scan(WebAppResourcesScanner.java:79)
      at com.sun.jersey.api.core.ScanningResourceConfig.init(ScanningResourceConfig.java:35)
      at com.sun.jersey.api.core.servlet.WebAppResourceConfig.init(WebAppResourceConfig.java:102)
      at com.sun.jersey.api.core.servlet.WebAppResourceConfig.<init>(WebAppResourceConfig.java:89)
      at com.sun.jersey.api.core.servlet.WebAppResourceConfig.<init>(WebAppResourceConfig.java:74)
      at com.sun.jersey.spi.container.servlet.WebComponent.getWebAppResourceConfig(WebComponent.java:672)
      at com.sun.jersey.spi.container.servlet.ServletContainer.getDefaultResourceConfig(ServletContainer.java:414)
      at com.sun.jersey.spi.container.servlet.ServletContainer.getDefaultResourceConfig(ServletContainer.java:581)
      at com.sun.jersey.spi.container.servlet.WebServletConfig.getDefaultResourceConfig(WebServletConfig.java:87)
      at com.sun.jersey.spi.container.servlet.WebComponent.createResourceConfig(WebComponent.java:703)
      at com.sun.jersey.spi.container.servlet.WebComponent.createResourceConfig(WebComponent.java:678)
      at com.sun.jersey.spi.container.servlet.WebComponent.init(WebComponent.java:203)
      at com.sun.jersey.spi.container.servlet.ServletContainer.init(ServletContainer.java:373)
      at com.sun.jersey.spi.container.servlet.ServletContainer.init(ServletContainer.java:556)
      at javax.servlet.GenericServlet.init(GenericServlet.java:158)
      at org.apache.catalina.core.StandardWrapper.initServlet(StandardWrapper.java:1231)
      at org.apache.catalina.core.StandardWrapper.loadServlet(StandardWrapper.java:1144)
      at org.apache.catalina.core.StandardWrapper.load(StandardWrapper.java:1031)
      at org.apache.catalina.core.StandardContext.loadOnStartup(StandardContext.java:4997)
      at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5289)
      at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
      at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:725)
      at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:701)
      at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:717)
      at org.apache.catalina.startup.HostConfig.deployDirectory(HostConfig.java:1101)
      at org.apache.catalina.startup.HostConfig$DeployDirectory.run(HostConfig.java:1813)
      at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
      at java.util.concurrent.FutureTask.run(FutureTask.java:266)
      at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
      at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
      at java.lang.Thread.run(Thread.java:745)

2. 解决

  • 本着有问题先google的态度去搜索了一番。Stack Overflow上有人给出的答案是因为:log4j-core-2.17.1.jar\\META-INF\\versions\\9下的内容不能兼容导致,可以直接从jar内删除这部分代码。
  • 按照此方法尝试后依然后异常抛出,于是想自己看下源码解决吧。
  • 通过异常栈信息可以定位到抛异常的代码类WebAppResourcesScanner
at com.sun.jersey.core.spi.scanning.JarFileScanner.scan(JarFileScanner.java:97)
at com.sun.jersey.spi.scanning.servlet.WebAppResourcesScanner$1.f(WebAppResourcesScanner.java:94)
at com.sun.jersey.core.util.Closing.f(Closing.java:71)
at com.sun.jersey.spi.scanning.servlet.WebAppResourcesScanner.scan(WebAppResourcesScanner.java:92)
at com.sun.jersey.spi.scanning.servlet.WebAppResourcesScanner.scan(WebAppResourcesScanner.java:79)
  • 找到jar中对应的类反编译查看scan方法。
private void scan(String root, final ScannerListener cfl) 
        Set<String> resourcePaths = this.sc.getResourcePaths(root);
        if (resourcePaths != null) 
            Iterator i$ = resourcePaths.iterator();

            while(i$.hasNext()) 
                final String resourcePath = (String)i$.next();
                if (resourcePath.endsWith("/")) 
                    this.scan(resourcePath, cfl);
                 else if (resourcePath.endsWith(".jar")) 
                    try 
                        (new Closing(this.sc.getResourceAsStream(resourcePath))).f(new Closure() 
                            public void f(InputStream in) throws IOException 
                                JarFileScanner.scan(in, "", cfl);
                            
                        );
                     catch (IOException var9) 
                        throw new ScannerException("IO error scanning jar " + resourcePath, var9);
                     
                 else if (cfl.onAccept(resourcePath)) 
                    try 
                        (new Closing(this.sc.getResourceAsStream(resourcePath))).f(new Closure() 
                            public void f(InputStream in) throws IOException 
                                cfl.onProcess(resourcePath, in);
                            
                        );
                     catch (IOException var7) 
                        throw new ScannerException("IO error scanning resource " + resourcePath, var7);
                     catch (Exception var8) 
                        this.LOGGER.log(Level.WARNING, "Failed to scan path: " + resourcePath, var8);
                    
                
            
        

    
  • 可以大概看到这部分的代码逻辑就是扫面传入的路径中所有的jar文件读取需要的配置,结合类名和代码基本可以判断出是为了加载web source的一些资源信息,而抛出异常的代码应该是在这部分JarFileScanner.scan(in, "", cfl)
try 
	(new Closing(this.sc.getResourceAsStream(resourcePath))).f(new Closure() 
	    public void f(InputStream in) throws IOException 
	        JarFileScanner.scan(in, "", cfl);
	    
	);
 catch (IOException var9) 
	throw new ScannerException("IO error scanning jar " + resourcePath, var9);

  • 可以看到这里只catch (IOException var9),并没有catch到我们遇到的java.lang.ArrayIndexOutOfBoundsException,这应该就是不兼容导致程序无法正常运行的直接原因。
  • 于是我追加了异常捕获来处理异常。
try 
    (new Closing(this.sc.getResourceAsStream(resourcePath))).f(new Closure() 
        public void f(InputStream in) throws IOException 
            JarFileScanner.scan(in, "", cfl);
        
    );
 catch (IOException var9) 
    throw new ScannerException("IO error scanning jar " + resourcePath, var9);
 catch (Exception var10) 
	// 这里捕获到任何异常都打印日志,标明扫描失败的jar
    this.LOGGER.log(Level.WARNING, "Failed to scan path: " + resourcePath, var10);

  • 修改完后编译出class文件替换到jersey-servlet-1.10.jar,将新的jersey更新到项目lib下。
  • 重新部署运行,成功启动,并打印如下log.
com.sun.jersey.spi.scanning.servlet.WebAppResourcesScanner localhost-startStop-1 WARNING: Failed to scan path: /WEB-INF/lib/log4j-api-2.17.1.jar (java.lang.ArrayIndexOutOfBoundsException)\\n  java.lang.ArrayIndexOutOfBoundsException
com.sun.jersey.spi.scanning.servlet.WebAppResourcesScanner localhost-startStop-1 WARNING: Failed to scan path: /WEB-INF/lib/log4j-core-2.17.1.jar (java.lang.ArrayIndexOutOfBoundsException)\\n  java.lang.ArrayIndexOutOfBoundsException
com.sun.jersey.spi.scanning.servlet.WebAppResourcesScanner localhost-startStop-1 WARNING: Failed to scan path: /WEB-INF/lib/log4j-1.2-api-2.17.1.jar (java.lang.ArrayIndexOutOfBoundsException: 14385)\\n  java.lang.ArrayIndexOutOfBoundsException: 14385\\n      at org.objectweb.asm.ClassReader.<init>(Unknown Source)\\n      at org.objectweb.asm.ClassReader.<init>(Unknown Source)\\n      at org.objectweb.asm.ClassReader.<init>(Unknown Source)\\n      at com.sun.jersey.spi.scanning.AnnotationScannerListener.onProcess(AnnotationScannerListener.java:133)\\n      at com.sun.jersey.core.spi.scanning.JarFileScanner.scan(JarFileScanner.java:97)\\n      at com.sun.jersey.spi.scanning.servlet.WebAppResourcesScanner$1.f(WebAppResourcesScanner.java:94)\\n      at com.sun.jersey.core.util.Closing.f(Closing.java:71)\\n      at com.sun.jersey.spi.scanning.servlet.WebAppResourcesScanner.scan(WebAppResourcesScanner.java:53)\\n      at com.sun.jersey.spi.scanning.servlet.WebAppResourcesScanner.scan(WebAppResourcesScanner.java:37)\\n      at com.sun.jersey.api.core.ScanningResourceConfig.init(ScanningResourceConfig.java:80)\\n      at com.sun.jersey.api.core.servlet.WebAppResourceConfig.init(WebAppResourceConfig.java:102)\\n      at com.sun.jersey.api.core.servlet.WebAppResourceConfig.<init>(WebAppResourceConfig.java:89)\\n      at com.sun.jersey.api.core.servlet.WebAppResourceConfig.<init>(WebAppResourceConfig.java:74)\\n      at com.sun.jersey.spi.container.servlet.WebComponent.getWebAppResourceConfig(WebComponent.java:672)\\n      at com.sun.jersey.spi.container.servlet.ServletContainer.getDefaultResourceConfig(ServletContainer.java:414)\\n      at com.sun.jersey.spi.container.servlet.ServletContainer.getDefaultResourceConfig(ServletContainer.java:581)\\n      at com.sun.jersey.spi.container.servlet.WebServletConfig.getDefaultResourceConfig(WebServletConfig.java:87)\\n      at com.sun.jersey.spi.container.servlet.WebComponent.createResourceConfig(WebComponent.java:703)\\n      at com.sun.jersey.spi.container.servlet.WebComponent.createResourceConfig(WebComponent.java:678)\\n      at com.sun.jersey.spi.container.servlet.WebComponent.init(WebComponent.java:203)\\n      at com.sun.jersey.spi.container.servlet.ServletContainer.init(ServletContainer.java:373)\\n      at com.sun.jersey.spi.container.servlet.ServletContainer.init(ServletContainer.java:556)\\n      at javax.servlet.GenericServlet.init(GenericServlet.java:158)\\n      at org.apache.catalina.core.StandardWrapper.initServlet(StandardWrapper.java:1231)\\n      at org.apache.catalina.core.StandardWrapper.loadServlet(StandardWrapper.java:1144)\\n      at org.apache.catalina.core.StandardWrapper.load(StandardWrapper.java:1031)\\n      at org.apache.catalina.core.StandardContext.loadOnStartup(StandardContext.java:4997)\\n      at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5289)\\n      at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)\\n      at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:725)\\n      at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:701)\\n      at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:717)\\n      at org.apache.catalina.startup.HostConfig.deployDirectory(HostConfig.java:1101)\\n      at org.apache.catalina.startup.HostConfig$DeployDirectory.run(HostConfig.java:1813)\\n      at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)\\n      at java.util.concurrent.FutureTask.run(FutureTask.java:266)\\n      at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)\\n      at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)\\n      at java.lang.Thread.run(Thread.java:745)
  • 这样的改动相当于在扫描web source的时候跳过了log4j的jar,这应该是没问题的,log4j里并不包含任何web项目的内容,只是一个打日志的工具包。
  • 至此问题解决。

3. 后记

  • 后来回想了一下,也许第一种google到的办法也许也可以解决。因为一共3个jar,而我只修改其中的一个,如果都处理的话也许是可以的,没有继续尝试。
  • 修改的代码片段是用于扫描jar的,所以扫描路径肯定是可以配置的,我找到了对应的参数名,默认是扫描/WEB-INF/lib/*也就是所有jar,如果我通过参数去指定哪些jar需要被扫描也是可以的,但没这么做。这个项目不太了解,并不确定需要扫描哪些jar,如果我们所有的jar都加上会是很长的参数,而只为了跳过log4j实在是太麻烦了不易于维护。

以上是关于log4j 1.x 升级到 2.x 依赖不兼容问题的解决的主要内容,如果未能解决你的问题,请参考以下文章

log4j 1.x 升级到 2.x 依赖不兼容问题的解决

log4j笔记:升级2.X版本的日志滚动问题

RxJava 2.0 简单介绍

log4j升级为log4j2(无需改动代码)

细说log4j之概述

Python Tensorflow1.x升级到2.x低阶API实践