環境介紹
Spring Boot 2.0.2 Java 8
任務描述
由於Spring Boot 2.0 默認情況下是使用logback作為日志系統的,這里希望切換到log4j2.
pom.xml內容定義
這里在pom.xml新增了spring-boot中的日志組件
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
第一次運行碰到的錯誤信息
直接運行,在控制台中報出如下的錯誤信息:
SLF4J: Class path contains multiple SLF4J bindings.
SLF4J: Found binding in [jar:file:/D:/DevSpace/M2Space/ch/qos/logback/logback-classic/1.2.3/logback-classic-1.2.3.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:/D:/DevSpace/M2Space/org/apache/logging/log4j/log4j-slf4j-impl/2.10.0/log4j-slf4j-impl-2.10.0.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 [ch.qos.logback.classic.util.ContextSelectorStaticBinder]
13:55:11.713 [main] DEBUG org.springframework.boot.devtools.settings.DevToolsSettings - Included patterns for restart : []
13:55:11.721 [main] DEBUG org.springframework.boot.devtools.settings.DevToolsSettings - Excluded patterns for restart : [/spring-boot-actuator/target/classes/, /spring-boot-devtools/target/classes/, /spring-boot/target/classes/, /spring-boot-starter-[\w-]+/, /spring-boot-autoconfigure/target/classes/, /spring-boot-starter/target/classes/]
13:55:11.722 [main] DEBUG org.springframework.boot.devtools.restart.ChangeableUrls - Matching URLs for reloading : [file:/D:/CodeSpace/photobuyapi/target/classes/]
Logging system failed to initialize using configuration from 'classpath:log4j2.xml'
java.lang.IllegalStateException: Logback configuration error detected:
ERROR in ch.qos.logback.core.joran.spi.Interpreter@3:17 - no applicable action for [properties], current ElementPath is [[configuration][properties]]
ERROR in ch.qos.logback.core.joran.spi.Interpreter@4:35 - no applicable action for [property], current ElementPath is [[configuration][properties][property]]
ERROR in ch.qos.logback.core.joran.spi.Interpreter@7:16 - no applicable action for [appenders], current ElementPath is [[configuration][appenders]]
ERROR in ch.qos.logback.core.joran.spi.Interpreter@9:53 - no applicable action for [Console], current ElementPath is [[configuration][appenders][Console]]
ERROR in ch.qos.logback.core.joran.spi.Interpreter@10:92 - no applicable action for [PatternLayout], current ElementPath is [[configuration][appenders][Console][PatternLayout]]
ERROR in ch.qos.logback.core.joran.spi.Interpreter@16:91 - no applicable action for [RollingRandomAccessFile], current ElementPath is [[configuration][appenders][RollingRandomAccessFile]]
ERROR in ch.qos.logback.core.joran.spi.Interpreter@17:106 - no applicable action for [PatternLayout], current ElementPath is [[configuration][appenders][RollingRandomAccessFile][PatternLayout]]
ERROR in ch.qos.logback.core.joran.spi.Interpreter@18:23 - no applicable action for [Policies], current ElementPath is [[configuration][appenders][RollingRandomAccessFile][Policies]]
ERROR in ch.qos.logback.core.joran.spi.Interpreter@19:45 - no applicable action for [TimeBasedTriggeringPolicy], current ElementPath is [[configuration][appenders][RollingRandomAccessFile][Policies][TimeBasedTriggeringPolicy]]
ERROR in ch.qos.logback.core.joran.spi.Interpreter@21:48 - no applicable action for [DefaultRolloverStrategy], current ElementPath is [[configuration][appenders][RollingRandomAccessFile][DefaultRolloverStrategy]]
ERROR in ch.qos.logback.core.joran.spi.Interpreter@25:14 - no applicable action for [loggers], current ElementPath is [[configuration][loggers]]
ERROR in ch.qos.logback.core.joran.spi.Interpreter@27:58 - no applicable action for [logger], current ElementPath is [[configuration][loggers][logger]]
ERROR in ch.qos.logback.core.joran.spi.Interpreter@30:74 - no applicable action for [logger], current ElementPath is [[configuration][loggers][logger]]
ERROR in ch.qos.logback.core.joran.spi.Interpreter@31:42 - no applicable action for [appender-ref], current ElementPath is [[configuration][loggers][logger][appender-ref]]
ERROR in ch.qos.logback.core.joran.spi.Interpreter@32:46 - no applicable action for [appender-ref], current ElementPath is [[configuration][loggers][logger][appender-ref]]
ERROR in ch.qos.logback.core.joran.spi.Interpreter@36:28 - no applicable action for [root], current ElementPath is [[configuration][loggers][root]]
ERROR in ch.qos.logback.core.joran.spi.Interpreter@37:42 - no applicable action for [appender-ref], current ElementPath is [[configuration][loggers][root][appender-ref]]
ERROR in ch.qos.logback.core.joran.spi.Interpreter@38:46 - no applicable action for [appender-ref], current ElementPath is [[configuration][loggers][root][appender-ref]]
at org.springframework.boot.logging.logback.LogbackLoggingSystem.loadConfiguration(LogbackLoggingSystem.java:166)
at org.springframework.boot.logging.AbstractLoggingSystem.initializeWithSpecificConfig(AbstractLoggingSystem.java:67)
at org.springframework.boot.logging.AbstractLoggingSystem.initialize(AbstractLoggingSystem.java:57)
at org.springframework.boot.logging.logback.LogbackLoggingSystem.initialize(LogbackLoggingSystem.java:114)
at org.springframework.boot.context.logging.LoggingApplicationListener.initializeSystem(LoggingApplicationListener.java:269)
at org.springframework.boot.context.logging.LoggingApplicationListener.initialize(LoggingApplicationListener.java:237)
at org.springframework.boot.context.logging.LoggingApplicationListener.onApplicationEnvironmentPreparedEvent(LoggingApplicationListener.java:200)
at org.springframework.boot.context.logging.LoggingApplicationListener.onApplicationEvent(LoggingApplicationListener.java:173)
at org.springframework.context.event.SimpleApplicationEventMulticaster.doInvokeListener(SimpleApplicationEventMulticaster.java:172)
at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:165)
at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:139)
at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:127)
at org.springframework.boot.context.event.EventPublishingRunListener.environmentPrepared(EventPublishingRunListener.java:74)
at org.springframework.boot.SpringApplicationRunListeners.environmentPrepared(SpringApplicationRunListeners.java:54)
at org.springframework.boot.SpringApplication.prepareEnvironment(SpringApplication.java:358)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:317)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1255)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1243)
at com.jd.ai.cv.api.PubAPIApplication.main(PubAPIApplication.java:17)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:49)
從上述日志中,可以發現,默認使用了logback-classic的日志組件,所以和這里引用的logging組件發生了沖突。
注釋掉logback
在pom.xml中注釋掉logback.xml中的logback的引用類庫,在Eclipse中的Dependency Hierarchy中搜索logback類庫,右擊選中,選擇Exclude Maven Artifact…操作,就將exclude規則寫到了pom.xml中了。
從上圖中可以看到,這里是Logging組件本身對於logback有依賴,這里應該是從Logging中將logback的依賴包Exclude掉,但是…..問題出現了。在pom.xml,將看到如下的Exclude規則:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
雖然從圖中看到的和pom.xml中的實際Exclude規則是不一致的,實際的Exclude規則被放到了Web的組件中了, 那為什么會是這樣的?
那只能有一種解釋,Web組件實質上依賴於Logging組件的,在組件的設計中,只是將logback類包放入到了Logging組件中,但是Spring Boot中的Web組件是直接使用logback類庫的。所以,Exclude規則是發生在了Spring Boot的Web組件中。
第二次執行碰到的問題
在Exclude了logback之后,重新執行整個項目,希望可以正常啟動項目,結果問題依然,只是錯誤信息不同了。。。。。
Exception in thread "main" java.lang.StackOverflowError
at org.apache.logging.log4j.util.StackLocator.getCallerClass(StackLocator.java:112)
at org.apache.logging.log4j.util.StackLocator.getCallerClass(StackLocator.java:125)
at org.apache.logging.log4j.util.StackLocatorUtil.getCallerClass(StackLocatorUtil.java:55)
at org.apache.logging.slf4j.Log4jLoggerFactory.getContext(Log4jLoggerFactory.java:42)
at org.apache.logging.log4j.spi.AbstractLoggerAdapter.getLogger(AbstractLoggerAdapter.java:46)
at org.apache.logging.slf4j.Log4jLoggerFactory.getLogger(Log4jLoggerFactory.java:29)
at org.slf4j.LoggerFactory.getLogger(LoggerFactory.java:358)
at org.apache.logging.slf4j.SLF4JLoggerContext.getLogger(SLF4JLoggerContext.java:39)
at org.apache.logging.slf4j.Log4jLoggerFactory.newLogger(Log4jLoggerFactory.java:37)
at org.apache.logging.slf4j.Log4jLoggerFactory.newLogger(Log4jLoggerFactory.java:29)
at org.apache.logging.log4j.spi.AbstractLoggerAdapter.getLogger(AbstractLoggerAdapter.java:52)
at org.apache.logging.slf4j.Log4jLoggerFactory.getLogger(Log4jLoggerFactory.java:29)
at org.slf4j.LoggerFactory.getLogger(LoggerFactory.java:358)
at org.apache.logging.slf4j.SLF4JLoggerContext.getLogger(SLF4JLoggerContext.java:39)
at org.apache.logging.slf4j.Log4jLoggerFactory.newLogger(Log4jLoggerFactory.java:37)
at org.apache.logging.slf4j.Log4jLoggerFactory.newLogger(Log4jLoggerFactory.java:29)
at org.apache.logging.log4j.spi.AbstractLoggerAdapter.getLogger(AbstractLoggerAdapter.java:52)
at org.apache.logging.slf4j.Log4jLoggerFactory.getLogger(Log4jLoggerFactory.java:29)
at org.slf4j.LoggerFactory.getLogger(LoggerFactory.java:358)
從錯誤信息中可以發現,發生死循環了,哪里來的死循環呢?是Log4j的實例化過程中死循環。
問題的解決
在經歷了若干個分鍾的無奈思考之后,於是想嘗試一下。由於Web組件中直接依賴了Logging組件,導致了整個問題的發生,是否可以直接將Logging組件從Web中Exclude掉,然后在pom.xml中直接引入Logging組件。是否就可以解決問題?
大膽假設,小心求證,來吧。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
<exclusion>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
</exclusion>
</exclusions>
</dependency>
如果上述的方法依然無法解決您的問題,就基於如下的方式,直接排除掉logging的組件:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
重新執行
系統正常啟動,問題已解決。
總結
這里的問題症結點在logging組件與log4j2組件在Spring Boot體系中的彼此沖突問題,這里只能Exclude掉其中一個,方可正常地啟動系統。