개발/Spring Boot

MDC를 활용한 추적 가능한 로그 남기기

registry 2024. 4. 18. 17:56

## 에러는 보이는데 원인이 안 보였다

 

요청, 응답 로그는 잘 찍히는데 서로 엮이지 않았다. 
400이 났다는 건 보이는데,  
어떤 요청의 결과인지 바로 연결되지 않았다.

## 눈으로는 구분이 안 됐다

 

로그는 이런 식이었다.

(이해를 돕기 위해 예제에서는 요청(Request)과 응답(Response) 로그만 사용했습니다.
실제 서비스 환경에서는 이 외에도 인증 정보, 파라미터, 내부 처리 과정, 외부 API 호출 등 다양한 로그를 함께 기록합니다.)

- Before Request
- Before Request
- After Request status = 500
- Before Request
- After Request status = 200
- After Request status = 400

 

어떤 요청이 어떤 응답을 주는지 한 눈에 들어오지 않는다.
사람이 눈으로 따라가기엔 무리였다.

## 기준을 하나 만들기로 했다


요청 단위로 로그를 묶을 수 있어야 했다.  
식별 가능한 키가 필요했다.

요청이 시작될 때 하나 만들고,  
끝날 때까지 같이 가는 값.  
이 조건에 딱 맞는 게 MDC였다.

## MDC를 적용한 뒤


요청마다 UUID 하나를 붙였다.  
로그에 항상 같이 찍히게 했다.

그 뒤 로그는 이렇게 바뀌었다.

- [uuid_1] Before ...
- [uuid_2] Before ...
- [uuid_1] After status = 500
- [uuid_3] Before ...
- [uuid_2] After status = 200
- [uuid_3] After status = 400

400 에러를 보면 uuid 하나만 따라가면 됐다.  
앞뒤 로그가 바로 연결됐다.  
이제 어떤 요청에 대해 로그를 추적하려면 uuid기준으로 검색하면 쉽게 추적할 수 있게 되었다.

## 적용은 필터에서 했다


요청 시작 지점이 명확해야 했다.  
그래서 필터를 선택했다.

요청이 들어오면 UUID를 만들고,  
MDC에 넣는다.  
요청이 끝나면 제거한다.

@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
class MDCFilter : OncePerRequestFilter() {

    companion object {
        const val MDC_REQUEST_ID_KEY = "requestId"
    }

    override fun doFilterInternal(
        request: HttpServletRequest,
        response: HttpServletResponse,
        filterChain: FilterChain
    ) {
        val requestId = UUID.randomUUID().toString().replace("-", "")
        MDC.put(MDC_REQUEST_ID_KEY, requestId)

        try {
            filterChain.doFilter(request, response)
        } finally {
            MDC.remove(MDC_REQUEST_ID_KEY)
        }
    }
}

 

# application.yml
logging:
  pattern:
    console: "%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger - [%X{requestId:-startup}] %msg%n"