Wildfly 23 서버
로컬에서 잘 돌던 프로젝트를 개발 서버에 올리니 에러를 뿜는다!
(가장 아래 해결 방법 있습니다.)
환경 - 로컬 : Spring 4 + 톰캣8.5 / 개발 서버 : Spring 4 + Wildfly 23
요즘에는 거의
취미로 하는 개인 프로젝트를 Sprin-Boot(embedded tomcat), node.js 를 이용해서 개발하고 있고,
일로 하는 프로젝트는 WAS 초기 설정을 제가 할 필요가 없어 불편함을 느끼지 못하고 있었습니다.
(일로 하는 프로젝트도 거의 Tomcat이나 JEUS를 사용...)
그러다 얼마전 시작한 프로젝트는 Wildfly 를 사용할 것이라고 들었고,
초기 설정에 대한 부분도 사실 고민하지 않고 있었습니다.
개발 서버에 현재까지 작업한 내용을 적용하고 리뷰를 하기 위해 자신있게 서버에 프로젝트를 돌렸습니다.
너무 자신있었나봅니다.
org.slf4j.impl.Slf4jLoggerFactory cannot be cast to ch.qos.logback.classic.LoggerContext
지금까지 로컬에서 너무나도 잘 되던 Logging 관련 부분에서 에러가 발생하다니...
(프로젝트 내에서 slf4j , log4j , loback 와 Wildfly 에서 사용하는 slf4j 가 충돌이 난 것 같습니다.)
혹시나 dependency 에 버전을 잘못 작성했나... 소스 코드 어딘가에서 작성을 잘못했나 싶었는데 당연히 아니었구요.
wildfly + spring + slf4 이 조합에는 어떠한 비밀이 있구나!
일단, 로컬 환경에서 동일한 에러를 확인할 수 있도록 구성해야합니다.
Spring Project
<!-- pom.xml -->
...
<!-- Logging -->
<log4j-version>1.2.17</log4j-version>
<org.slf4j-version>1.7.31</org.slf4j-version>
...
<!-- Logging -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${org.slf4j-version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>${org.slf4j-version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>${org.slf4j-version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>${log4j-version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.0.1</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.0.1</version>
</dependency>
<dependency>
<groupId>org.bgee.log4jdbc-log4j2</groupId>
<artifactId>log4jdbc-log4j2-jdbc4</artifactId>
<version>1.16</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>1.2.4</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.4</version>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.logback-extensions</groupId>
<artifactId>logback-ext-spring</artifactId>
<version>0.1.4</version>
</dependency>
<!-- context-datasource.xml -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="net.sf.log4jdbc.sql.jdbcapi.DriverSpy" />
<property name="url" value="${db.url}" />
<property name="username" value="${db.username}" />
<property name="password" value="${db.password}" />
</bean>
<!-- src/main/resources/logback.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="30 seconds">
<property scope="context" name="log_home" value="/logs" />
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>[%d{yyyy-MM-dd HH:mm:ss.SSS}][%-5level][%logger{36}.%method:line%line] - %msg%n</pattern>
</encoder>
</appender>
<appender name="file" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log_home}/로그파일명.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${log_home}/로그파일명.%d{yyyy-MM-dd}.log</fileNamePattern>
<maxHistory>180</maxHistory>
</rollingPolicy>
<encoder>
<pattern>[%d{yyyy-MM-dd HH:mm:ss.SSS}][%-5level][%logger{36}.%method:line%line] - %msg%n</pattern>
</encoder>
</appender>
<logger name="org.springframework.core" level="info" additivity="false">
<appender-ref ref="file" />
<appender-ref ref="console" />
</logger>
<logger name="org.springframework.beans" level="info" additivity="false">
<appender-ref ref="file" />
<appender-ref ref="console" />
</logger>
<logger name="org.springframework.context" level="info" additivity="false">
<appender-ref ref="file" />
<appender-ref ref="console" />
</logger>
<logger name="org.springframework.web" level="info" additivity="false">
<appender-ref ref="file" />
<appender-ref ref="console" />
</logger>
<logger name="jdbc.sqlonly" level="info" additivity="false">
<appender-ref ref="file" />
<appender-ref ref="console" />
</logger>
<logger name="jdbc.sqltiming" level="info" additivity="false">
<appender-ref ref="file" />
<appender-ref ref="console" />
</logger>
<logger name="jdbc.audit" level="info" additivity="false">
<appender-ref ref="file" />
<appender-ref ref="console" />
</logger>
<logger name="jdbc.resultset" level="info" additivity="false">
<appender-ref ref="file" />
<appender-ref ref="console" />
</logger>
<logger name="jdbc.resultsettable" level="info" additivity="false">
<appender-ref ref="file" />
<appender-ref ref="console" />
</logger>
<logger name="jdbc.connection" level="info" additivity="false">
<appender-ref ref="file" />
<appender-ref ref="console" />
</logger>
<root level="info">
<appender-ref ref="console" />
<appender-ref ref="file" />
</root>
</configuration>
Wildfly 23 로컬 설정하고 로컬에서 에러를 확인해보자!
먼저 Wildfly 23 을 설치합니다.
아래 URL 로 접속하여 사진에 있는 부분을 클릭해 zip 파일을 다운 받습니다.
https://www.wildfly.org/downloads/
귀찮으신 분들은 아래 링크를 이용하세요
https://download.jboss.org/wildfly/23.0.2.Final/wildfly-23.0.2.Final.zip
그리고 압축을 푸시면 됩니다.
(다운로드 폴더 말고 작업공간에 푸시길 추천합니다.)
이클립스를 실행합니다.
JBoss Tools 4.16.0.Final 을 설치합니다.
(글 작성일 2021-07-31 기준입니다.)
이클립스 재시작이 되고난 뒤에 Server 탭에서 오른쪽 클릭을 합니다.
글 작성일 기준 JBoss Tools 를 설치했기 때문에 최신 버전은 WildFly 20 으로 보이지만 글 작성일 후에 실습하시는 분들은 WildFly 버전이 다를 수 있습니다.
Create new runtime (next page) 를 꼭 선택하세요
Browse 를 클릭하고 WildFly 23 을 압축 해제한 폴더를 선택하세요.
준비는 다 되었습니다.
서버를 실행하면 아래와 같은 에러가 발생합니다.
00:34:05,571 ERROR [org.jboss.msc.service.fail] (ServerService Thread Pool -- 80) MSC000001: Failed to start service jboss.deployment.unit."스프링프로젝트명-1.0.0-BUILD-SNAPSHOT.war".undertow-deployment: org.jboss.msc.service.StartException in service jboss.deployment.unit."스프링프로젝트명-1.0.0-BUILD-SNAPSHOT.war".undertow-deployment: java.lang.RuntimeException: java.lang.ClassCastException: org.slf4j.impl.Slf4jLoggerFactory cannot be cast to ch.qos.logback.classic.LoggerContext
at org.wildfly.extension.undertow.deployment.UndertowDeploymentService$1.run(UndertowDeploymentService.java:81)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at org.jboss.threads.ContextClassLoaderSavingRunnable.run(ContextClassLoaderSavingRunnable.java:35)
at org.jboss.threads.EnhancedQueueExecutor.safeRun(EnhancedQueueExecutor.java:1990)
at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.doRunTask(EnhancedQueueExecutor.java:1486)
at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1377)
at java.lang.Thread.run(Thread.java:748)
at org.jboss.threads.JBossThread.run(JBossThread.java:513)
Caused by: java.lang.RuntimeException: java.lang.ClassCastException: org.slf4j.impl.Slf4jLoggerFactory cannot be cast to ch.qos.logback.classic.LoggerContext
at io.undertow.servlet.core.DeploymentManagerImpl.deploy(DeploymentManagerImpl.java:257)
at org.wildfly.extension.undertow.deployment.UndertowDeploymentService.startContext(UndertowDeploymentService.java:96)
at org.wildfly.extension.undertow.deployment.UndertowDeploymentService$1.run(UndertowDeploymentService.java:78)
... 8 more
Caused by: java.lang.ClassCastException: org.slf4j.impl.Slf4jLoggerFactory cannot be cast to ch.qos.logback.classic.LoggerContext
at ch.qos.logback.ext.spring.LogbackConfigurer.initLogging(Unknown Source)
at ch.qos.logback.ext.spring.web.WebLogbackConfigurer.initLogging(Unknown Source)
at ch.qos.logback.ext.spring.web.LogbackConfigListener.contextInitialized(Unknown Source)
at io.undertow.servlet.core.ApplicationListeners.contextInitialized(ApplicationListeners.java:187)
at io.undertow.servlet.core.DeploymentManagerImpl$1.call(DeploymentManagerImpl.java:219)
at io.undertow.servlet.core.DeploymentManagerImpl$1.call(DeploymentManagerImpl.java:187)
at io.undertow.servlet.core.ServletRequestContextThreadSetupAction$1.call(ServletRequestContextThreadSetupAction.java:42)
at io.undertow.servlet.core.ContextClassLoaderSetupAction$1.call(ContextClassLoaderSetupAction.java:43)
at org.wildfly.extension.undertow.security.SecurityContextThreadSetupAction.lambda$create$0(SecurityContextThreadSetupAction.java:105)
at org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction.lambda$create$0(UndertowDeploymentInfoService.java:1530)
at org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction.lambda$create$0(UndertowDeploymentInfoService.java:1530)
at org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction.lambda$create$0(UndertowDeploymentInfoService.java:1530)
at org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction.lambda$create$0(UndertowDeploymentInfoService.java:1530)
at io.undertow.servlet.core.DeploymentManagerImpl.deploy(DeploymentManagerImpl.java:255)
... 10 more
00:34:05,577 ERROR [org.jboss.as.controller.management-operation] (Controller Boot Thread) WFLYCTL0013: Operation ("deploy") failed - address: ([("deployment" => "스프링프로젝트명-1.0.0-BUILD-SNAPSHOT.war")]) - failure description: {"WFLYCTL0080: Failed services" => {"jboss.deployment.unit.\"스프링프로젝트명-1.0.0-BUILD-SNAPSHOT.war\".undertow-deployment" => "java.lang.RuntimeException: java.lang.ClassCastException: org.slf4j.impl.Slf4jLoggerFactory cannot be cast to ch.qos.logback.classic.LoggerContext
Caused by: java.lang.RuntimeException: java.lang.ClassCastException: org.slf4j.impl.Slf4jLoggerFactory cannot be cast to ch.qos.logback.classic.LoggerContext
Caused by: java.lang.ClassCastException: org.slf4j.impl.Slf4jLoggerFactory cannot be cast to ch.qos.logback.classic.LoggerContext"}}
00:34:05,670 INFO [org.jboss.as.server] (ServerService Thread Pool -- 45) WFLYSRV0010: Deployed "스프링프로젝트명-1.0.0-BUILD-SNAPSHOT.war" (runtime-name : "스프링프로젝트명-1.0.0-BUILD-SNAPSHOT.war")
00:34:05,672 INFO [org.jboss.as.controller] (Controller Boot Thread) WFLYCTL0183: Service status report
WFLYCTL0186: Services which failed to start: service jboss.deployment.unit."스프링프로젝트명-1.0.0-BUILD-SNAPSHOT.war".undertow-deployment: java.lang.RuntimeException: java.lang.ClassCastException: org.slf4j.impl.Slf4jLoggerFactory cannot be cast to ch.qos.logback.classic.LoggerContext
WFLYCTL0448: 1 additional services are down due to their dependencies being missing or failed
에러 내용 중 가장 중요한 내용은 ClassCastException 입니다.
Caused by: java.lang.ClassCastException: org.slf4j.impl.Slf4jLoggerFactory cannot be cast to ch.qos.logback.classic.LoggerContext
결국 Wildfly 와 Spring Project 가 사용하는 slf4j 관련 라이브러리들의 충돌인 것 같습니다.
해결 방법
생각보다 쉽게 해결하였습니다.
src/main/webapp/WEB-INF/jboss-deployment-structure.xml
파일을 생성하면 Wildfly 가 기동되면서 해당 설정을 읽어 충돌을 제외하였습니다.
Wildfy가 제공하는 디펜던시(dependencies)를 배제하고 자체 디펜던시를 사용하도록 조정한 것 입니다.
<!-- src/main/webapp/WEB-INF/jboss-deployment-structure.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<jboss-deployment-structure>
<deployment>
<exclusions>
<!--
<module name="org.apache.commons.logging" />
<module name="org.apache.log4j" />
<module name="org.jboss.logging" />
<module name="org.jboss.logging.jul-to-slf4j-stub" />
<module name="org.jboss.logmanager" />
<module name="org.jboss.logmanager.log4j" />
-->
<module name="org.slf4j" />
<module name="org.slf4j.impl" />
</exclusions>
</deployment>
</jboss-deployment-structure>
이 설정을 했는데도 에러가 발생하면 에러 라인을 보고 대응하시면됩니다.
여담...
Wildfly context path 설정입니다.
별도의 설정 변경 없이 기본으로 서버를 사용하면 Wildfly 에서 정해주는 context path 를 따라야합니다.
src/main/webapp/WEB-INF/jboss-web.xml
파일을 생성하고 아래 내용을 작성하시면 됩니다.
(Wildfly 가 가장 먼저 읽는 설정 파일이라고 합니다.)
<?xml version="1.0" encoding="UTF-8"?>
<jboss-web>
<context-root>/</context-root>
</jboss-web>
'개발 > 서버' 카테고리의 다른 글
[Linux] 기본 명령어 (0) | 2021.02.16 |
---|---|
[Linux] 크론탭(Crontab) 사용하기 (0) | 2021.01.14 |
댓글