springboot 启动报 ERROR StatusLogger Log4j2 could not find a logging implementation. Please add log4j-core to the classpath. Using SimpleLogger to log to the console
报错信息看起来与log4j有关,项目没打算用log4j,也没有主动引入log4j的,困扰了好久,以下是解决思路
查看maven 发现log4j-api包被 springboot-starter下的springboot-starter-loggering引入的。
用idea ctrl+shift+f搜索报错信息,找到了报错日志点为 org.apache.logging.log4j.LogManager
的静态代码块中,由于if语句if (ProviderUtil.hasProviders())
判断为false,进入else语句打印出此log,猜测某个地方反射加载此类
查看堆栈信息,找到org.jboss.logging.LoggerProviders#findProvider
发现代码如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 try { return tryJBossLogManager (cl, null ) ; } catch (Throwable t) { }try { return tryLog4j2 (cl, null ) ; } catch (Throwable t) { }try { return tryLog4j (cl, null ) ; } catch (Throwable t) { }try { Class.forName("ch.qos.logback.classic.Logger" , false , cl); return trySlf4j (null ) ; } catch (Throwable t) { }return tryJDK (null ) ;
####代码去依次尝试获取jboss的log, log4j2, log4j, slf4j, jdklog来处理log.
通过反射来加载org.apache.logging.log4j.LogManager
时执行的上面静态代码块。 1 2 3 4 5 6 7 8 9 private static LoggerProvider tryLog4j2(final ClassLoader cl, final String via) throws ClassNotFoundException { Class .forName("org.apache.logging.log4j.Logger" , true , cl); Class .forName("org.apache.logging.log4j.LogManager" , true , cl); Class .forName("org.apache.logging.log4j.spi.AbstractLogger" , true , cl); LoggerProvider provider = new Log4j2LoggerProvider(); logProvider(provider, via); return provider; }
查看报错点的if判断if (ProviderUtil.hasProviders())
里面的代码如下 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 protected static final String PROVIDER_RESOURCE = "META-INF/log4j-provider.properties" ; public static boolean hasProviders () { lazyInit(); return !PROVIDERS.isEmpty(); } protected static void lazyInit () { if (instance == null ) { try { STARTUP_LOCK.lockInterruptibly(); try { if (instance == null ) { instance = new ProviderUtil (); } } finally { STARTUP_LOCK.unlock(); } } catch (final InterruptedException e) { LOGGER.fatal("Interrupted before Log4j Providers could be loaded." , e); Thread.currentThread().interrupt(); } } } private ProviderUtil () { for (final LoaderUtil.UrlResource resource : LoaderUtil.findUrlResources(PROVIDER_RESOURCE)) { loadProvider(resource.getUrl(), resource.getClassLoader()); } }
调用了ProviderUtil的构造方法,其中尝试获取配置文件META-INF/log4j-provider.properties
但是获取为空 这样PROVIDERS
这个list的值就为空,从而hasProviders
方法返回false。
但是我的另一个项目启动时却不会打印出这样的错误log信息,按照上面的思路观察源码,发现另一个项目引入的是log4j-api 2.11.1
而报错的项目引入的是log4j-api 2.8.1
,其中 2.11.1的ProviderUtil构造方法变成了这样 1 2 3 4 5 6 7 8 9 10 11 12 13 private ProviderUtil () { for (final ClassLoader classLoader : LoaderUtil.getClassLoaders()) { try { loadProviders (classLoader); } catch (final Throwable ex) { LOGGER.debug ("Unable to retrieve provider from ClassLoader {}", classLoader, ex); } } for (final LoaderUtil.UrlResource resource : LoaderUtil.findUrlResources(PROVIDER_RESOURCE)) { loadProvider (resource.getUrl(), resource.getClassLoader ()); } }
更高版本的log4j-api中适应多个classLoad尝试加载providers,在这里成功的找到了一个,随意if没有报错 观察maven的配置中并没有引入log4j的包,然后查看父项目的maven配置 1 2 3 4 5 6 7 8 9 10 <dependencyManagement > <dependencies > <dependency > <groupId > org.apache.logging.log4j</groupId > <artifactId > log4j-api</artifactId > <version > 2.8.1</version > </dependency > </dependencies > </dependencyManagement >
#根据maven的依赖加载原理,依赖关系近的会覆盖远的,所以父项目的依赖覆盖了springboot-start中的依赖,导致版本不一致。 #删除父项目中引入的依赖即可,问题原因找到