문제의 시작

운영 로그는 많을수록 좋은 것이 아니다. 너무 많은 noise가 섞이면 정작 봐야 할 error와 request 흐름을 놓치게 된다. 이 글은 Spring Boot logging에서 특정 로그를 제외하며, 로그를 줄이는 일이 어떻게 운영 가독성과 연결되는지 정리한 기록이다.

스프링 부트 로그에 너무 많은 로그가 쌓여 로그를 뒤져 오류를 찾는 등 작업을 하고 싶을 때 불편한 경우가 있다. 혹은 최대 throughput 이 정해져 있는 AWS CloudWatch 에 로그가 쌓이는 경우 로그 누락 등의 치명적인 문제가 생길 수 있다.

특히 스프링 부트에서 쌓는 로그 뿐만 아니라 쿠버네티스나 ELB 에 배포 후 쌓이는 추가적인 부수 로그들이 있을 때 굳이 로그를 남기지 않는 판단을 할 수 있다.

다음은 간단하게 원하는 로그를 제외시키는 방법이다.

구현하면서 확인한 흐름

import ch.qos.logback.classic.spi.ILoggingEvent
import ch.qos.logback.core.filter.Filter
import ch.qos.logback.core.spi.FilterReply

class ContentBasedFilter : Filter<ILoggingEvent>() {
    override fun decide(event: ILoggingEvent): FilterReply {
        val message = event.formattedMessage
        if (message.contains("/manage/health") ||
            message.contains("kube-probe") ||
            message.contains("ELB-HealthChecker")) {
            return FilterReply.DENY
        }
        return FilterReply.NEUTRAL
    }
}

ch.qos.logback.classic.spi.ILoggingEvent 은 Logback 에 있는 인터페이스인데, 로깅과 관련된 모든 data 를 담고 있다.

ch.qos.logback.core.filter.Filter 는 Logback 의 abstract class 인데, 간단히 logging event 를 필터링하기 위해 사용한다.

ch.qos.logback.core.spi.FilterReply 는 enum 인데, DENY(이벤트 제외시키기), ACCEPT(이벤트 로깅하기), NEUTRAL(아무 동작하지 않기) 를 선택할 수 있다.

코드에서는 스프링 부트 헬스체크인 /manage/health 엔드포인트 관련 로깅, 쿠버네티스 배포 관련 kube-probe 관련 로깅, ELB 배포 관련 ELB-HealthChecker 로그를 DENY 옵션으로 제외시켰다.

ContentBasedFilter 는 어떤 @Configuration 혹은 @Bean 이 아니기 때문에 xml 설정 파일에 넣어주어야 하는데, 이것이 logback-spring.xml 이다.

<configuration>
    <springProperty scope="context" name="application-logging" source="spring.application.name" defaultValue=""/>

    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <!-- Translated pattern from your application.yml -->
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%5p] [${SPRING_APPLICATION_NAME:-},%X{X-B3-TraceId:-},%X{X-B3-SpanId:-}] [%15.15t] %-40.40logger{39} : %msg%n%replace(%ex){'\n','\r'}</pattern>
        </encoder>
        <!-- Apply the custom filter -->
        <filter class="${파일 디렉토리).ContentBasedFilter"/>
    </appender>

    <root level="INFO">
        <appender-ref ref="STDOUT"/>
    </root>
</configuration>

로깅 기준

Log filtering은 문제를 숨기기 위한 장치가 아니라, 중요한 신호를 더 잘 보이게 만드는 장치여야 한다. 어떤 로그를 버릴지 정할 때는 장애 분석에 필요한 정보인지, 반복적으로 같은 noise를 만드는지, 다른 metric이나 trace로 대체 가능한지 함께 판단해야 한다.

기본적인 포맷이다. filter class 에 이전에 만든 파일 경로를 넣어주었으며 pattern 에는 추가 커스텀 로깅을 위해 application.yaml 에서 가져온 SPRING_APPLICATION_NAME 과 traceId, spanId 등을 넣어주었다.