Log4J2 가 logback 보다 성능이 좋다는 팀의 가이드로 인해 Log4j2로 로그를 변경했다. 그런데 문제는 팀의 공용 라이브러리가 Logback 을 사용하다 보니 에러가 발생했다.
이런 에러는 보기만해도 잠이 깬다 ㅠㅠ
로그보기
LoggerFactory is not a Logback LoggerContext but Logback is on the classpath. 라는 에러가 발생했는데 앱을 실행하자 마자 이렇게 죽는 에러를 오랫만에 만났더니 매우 당황했다.
Situation
문제
•
LoggerFactory 에서 log의 구현체가 두 개 이상 발견되어 Spring application 을 실행하지 못했다.
•
Kotlin, SpringBoot 환경이다.
다음중 하나를 결정하여 실행한다.
1.
구현체 Logback을 제거하고 Log4j2 가 실행되도록 한다.
2.
현재 설정한 Log4j2 를 포기하고 기존의 logback 으로 돌아간다.
3.
공용 라이브러리의 Logback 을 제거한다.
시간이 충분하지 않으므로 최선의 방법인 1번을 목표로 trouble shooting 을 수행하지만, 만약 30분을 초과하면 2번을 선택한다.
3번을 선택하지 않는 이유는 공용 라이브러리이기 때문에 이를 가져다 사용하는 모든 application 에서 에러가 발생할 수 있으므로 이는 고려하지 않는다.
Task
build.gradle.kts 에서 Logback 을 exclude 하자.
이를 위한 blog posting 은 정말 많았다. 그리고 stack overflow 에도 이를 다루는 글이 많다.
SLF4J 란?
하지만, 해결법에 앞서 알아야 할 사실이 있다.
SLF4J 는 Simple Logging Facade for Java 의 약자로 자바 생태계의 logging library 의 interface 이다.
즉, 구현체가 없는 API 만 제공하기 때문에 구현체를 결정해줘야 한다.
구현체는 보통 다음과 같다.
•
java.util.logging
•
logback
•
reload4j
•
log4j
•
log4j2
Logback exclude 하기
지금 문제는 내 application 은 Log4j2 를 사용하는데 gradle 로 가져오는 library 가 logback 을 사용한다. 이 logback 을 classpath 에서 제거해야 한다.
dependencies {
implementation("ch.qos.logback:logback-core:1.2.3")
implementation("ch.qos.logback:logback-classic:1.2.3")
implementation("org.slf4j:slf4j-api:1.7.10")
}
Kotlin
복사
공용 라이브러리에서 사용하는 log 구현체
그래서 stack overflow의 글을 참조하여 다음과 같이 입력했다.
implementation("com.coway.commons:coway-commons:1.3.0") {
exclude("ch.qos.logback:logback-core")
exclude("ch.qos.logback:logback-classic")
}
Kotlin
복사
build.gradle.kts, 이렇게 하면 Kotlin gradle 에선 실행되지 않는다.
여기서 slf4j 를 exclude 하지 않은 이유는 interface의 public API를 제거하면 로깅이 안되기 때문이다.
구현체인 logback 만 제거해야 한다.
그런데 위와 같이 입력해도 제일 위의 그림과 같은 에러 메시지가 계속 발생한다.
exclude 를 잘못한 것일까?
Exclude 함수의 method signature
fun <T : ModuleDependency> T.exclude(
group: String? = null, module: String? = null
): T = uncheckedCast(exclude(excludeMapFor(group, module)))
Kotlin
복사
exclude 함수
이 함수를 이미지로 보기
이 함수를 살펴보면 method signature 로 group, module 을 받는다.
gradle kotlin 이기 때문에 exclude("ch.qos.logback:logback-core") 로 입력하면 module 매개변수는 null 이 할당된다.
Java 라면 overloading 함수가 없기 때문에 compile 에러가 발생했을 텐데 kotlin 은 null 이 입력된 것으로 간주된다.
따라서, kotlin gradle 의 경우 다음과 같이 작성해야 한다.
implementation("com.coway.commons:coway-commons:1.3.0") {
exclude("ch.qos.logback", "logback-core")
exclude("ch.qos.logback", "logback-classic")
}
Kotlin
복사
위와 같이 의존성을 갖는 library 의 group 과 module 을 분리해서 입력해야 exclude 가 어떤 dependency 를 제외할 것인이 특정할 수 있다.
Action
implementation("com.coway.commons:coway-commons:1.3.0") {
exclude("ch.qos.logback", "logback-core")
exclude("ch.qos.logback", "logback-classic")
}
Kotlin
복사
공용 라이브러리에 exclude 대상을 group, module 을 명시하여 특정했다.
Result
Problem resolved!
기존 라이브러리의 logback을 제외하고, log4j2 를 사용하여 성능과 기능을 함께 챙길 수 있게 되었다.