总结记一次log4j包冲突引发es类找不到的问题

Posted 常乐_smile

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了总结记一次log4j包冲突引发es类找不到的问题相关的知识,希望对你有一定的参考价值。

问题现象

某天,某个应用搞新的迭代,突然报ElasticSearch 7.17.5 相关操作都失败了,且问题是必现,本地启动也能稳定复现。组内小伙伴按照es jar包冲突排查了一番,无果,于是问题转交给我来排查。

错误信息是:

Caused by: java.lang.NoClassDefFoundError: Could not initialize class org.elasticsearch.client.RestHighLevelClient
	at cn.tss.yun.common.util.es.restclient.ElasticSearchRestClient.restHighLevelClient(ElasticSearchRestClient.java:128)
	at cn.tss.yun.common.util.es.restclient.ElasticSearchRestClient.<init>(ElasticSearchRestClient.java:78)
	at cn.tss.yun.common.util.es.restclient.ElasticSearchRestClientFactory.getEsRestClient(ElasticSearchRestClientFactory.java:65)
	at cn.tss.yun.tech.component.biz.service.impl.ElasticSearchService.getElasticSearchRestClient(ElasticSearchService.java:48)

排查分析

刚开始看到错误信息,注意,此处是NoClassDefFoundError,表明类是存在的,但是初始化时出错。如果是真的class文件都找不到,应该是报ClassNotFoundException.

我也按照ES jar包冲突的方向排查。一通解压反编译,排查下来,ES的相关的类和包,都没有版本冲突问题,和代码中使用的包路径,构造方法等,都是吻合的,但这里怪异的是,在其他应用中ES是正常使用的。

经过两次启动调试,笔者发现一个规律。应用在启动时,第一次访问ES相关接口,会先输出这么一段错误信息:

Caused by: java.lang.NoClassDefFoundError: Could not initialize class org.apache.logging.log4j.LogManager
at org.elasticsearch.client.RestHighLevelClient.(RestHighLevelClient.java:261)

于是瞬间,将焦点聚集在Log4j上。根据之前多次遇到类似的问题,这里很明显是log4j和logback的冲突。

进一步点开pom依赖证实,发现应用比其他应用多了:log4j-core、log4j-slf4j-impl 两个包。

瞬间豁然。表象上看是ES的RestHighLevelClient 类不存在,但是该类之所以不存在是因为,JVM第一次加载RestHighLevelClient class到内存中来时,由于class也要有初始化动作,该类有一个private static 的logger属性需要初始化,由于log4j和logback的冲突,导致该变量无法初始化,进一步导致RestHighLevelClient class 无法被加载成功。

line 261:private static final Logger logger = LogManager.getLogger(RestHighLevelClient.class);

程序上第二次、第三次、第N次访问接口时,报错就只报
Caused by: java.lang.NoClassDefFoundError: Could not initialize class org.elasticsearch.client.RestHighLevelClient
让人误以为是ES包冲突。

解决该问题,必须要对jvm 类加载机制有一定了解。

总结

直接原因

程序执行时创建ES 连接RestHighLevelClient 时,报该类不存在。

根本原因

hive-jdbc 升级版本到2.3.3后,间接依赖的log4j-core、log4j-slf4j-impl两个包和logback 的包有冲突。将引入log4j-core、log4j-slf4j-impl两个jar包排掉就可以了。

  • 真正错误原因分析

关键错误信息:

	at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
	at java.lang.Thread.run(Thread.java:748)
Caused by: java.lang.NoClassDefFoundError: Could not initialize class org.apache.logging.log4j.LogManager
	at org.elasticsearch.client.RestHighLevelClient.<clinit>(RestHighLevelClient.java:261)
	at cn.tss.yun.common.util.es.restclient.ElasticSearchRestClient.restHighLevelClient(ElasticSearchRestClient.java:128)
	at cn.tss.yun.common.util.es.restclient.ElasticSearchRestClient.<init>(ElasticSearchRestClient.java:78)

Caused by: java.lang.NoClassDefFoundError: Could not initialize class org.apache.logging.log4j.LogManager
at org.elasticsearch.client.RestHighLevelClient.(RestHighLevelClient.java:261)
这段核心错误的关键在于at org.elasticsearch.client.RestHighLevelClient.
clint是指jvm加载类初始化时,发生的错误。

JVM第一次加载RestHighLevelClient class到内存中来时,由于class初始化动作需要初始化static变量,该类有一个private static 的logger属性需要初始化,由于log4j和logback的冲突,导致该变量无法初始化,进一步导致RestHighLevelClient class 无法被加载成功。

  • 源码截图

解决方法

排除冲突的log4j-core、log4j-slf4j-impl两个jar包

 <dependency>
            <groupId>org.apache.hive</groupId>
            <artifactId>hive-jdbc</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>org.apache.curator</groupId>
                    <artifactId>curator-framework</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>org.apache.curator</groupId>
                    <artifactId>curator-recipes</artifactId>
                </exclusion>
                <exclusion>
                    <artifactId>*</artifactId>
                    <groupId>com.sun.jersey</groupId>
                </exclusion>
                <exclusion>
                    <artifactId>slf4j-log4j12</artifactId>
                    <groupId>org.slf4j</groupId>
                </exclusion>
                <exclusion>
                    <artifactId>log4j-core</artifactId>
                    <groupId>org.apache.logging.log4j</groupId>
                </exclusion>
                <exclusion>
                    <artifactId>log4j-slf4j-impl</artifactId>
                    <groupId>org.apache.logging.log4j</groupId>
                </exclusion>
            </exclusions>
        </dependency>

以上是关于总结记一次log4j包冲突引发es类找不到的问题的主要内容,如果未能解决你的问题,请参考以下文章

记一次 HttpClient 连接池参数引发的雪崩问题的定位分析解决过程!

记一次jar包冲突

SpringBoot中使用 Redisson 版本冲突导致的类找不到

Spark fasterxml包冲突异常解决方法

记一次日志采集问题:logstash没有向ES写入数据

记一次项目上线后Log4j2不输出日志的坑