| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | 5 | 6 | 7 |
| 8 | 9 | 10 | 11 | 12 | 13 | 14 |
| 15 | 16 | 17 | 18 | 19 | 20 | 21 |
| 22 | 23 | 24 | 25 | 26 | 27 | 28 |
- 오블완
- input
- 프론트엔드
- 깃
- 파이썬
- 코딩테스트 입문
- 티스토리챌린지
- JavaScript
- 깃허브
- 그리디 알고리즘
- 프로그래머스
- docker
- docker hub
- S3
- aws
- 경희대 K-MOOC
- react
- 배포
- 이것이 코딩테스트다
- k-mooc
- Git
- 백준
- 소프트웨어 설계와 파이썬
- 리액트
- 그리디
- 운영체제
- 경희대
- 자바스크립트
- 알고리즘
- GitHub
- Today
- Total
cowboysj
Spring Boot 애플리케이션 로깅 개선 : 요청 추적을 위한 MDC 도입 본문
1. 개요
현재 사용하고 있던 로깅 방식은 Logback을 기반으로 한 일반적인 형태였다.
<pattern>%green(%d{yyyy-MM-dd HH:mm:ss.SSS}) %magenta([%thread]) %highlight(%5level) %cyan(%logger) - %yellow(%msg%n)</pattern>
2025-07-02 10:13:28.171 [http-nio-8080-exec-2] INFO com.wematch.customer.service.partnerapp.MiaService - 현재 로그 방식
문제점
기존 로깅 방식에는 다음과 같은 문제점들이 있었다.
- 요청 흐름을 파악하기 어려움 - 하나의 HTTP 요청에서 발생한 여러 로그들을 연결해서 추적하기가 어려웠다.
- 요청 컨텍스트 부족 - 어떤 사용자의 어떤 요청인지 파악이 불가능해 CS 처리가 늦어지는 경우가 발생했다.
이러한 문제를 해결하기 위해 MDC(Mapped Diagnostic Context)를 활용한 로깅 시스템을 도입하기로 했다.
2. MDC란?
MDC(Mapped Diagnostic Context)는 멀티쓰레드 환경에서 로그에 컨텍스트 정보를 추가할 수 있게 해주는 SLF4J 기능이다.
각 쓰레드 별로 독립적인 맵을 유지해 요청 별 추적 정보를 저장할 수 있다.
3. 해결방안 : MDC 기반 요청 추적 시스템
MDC를 이용해 로그에 request_id, user_id를 추가하고, 요청 메서드, URL, IP, User-Agent도 함께 로깅하도록 개선했다.
MDCRequestLoggingFilter
HTTP 요청을 가로채서 MDC에 추적 정보를 삽입하는 필터로, Filter 구현체로서 HTTP 요청마다 MDC에 추적 정보를 삽입해 함께 출력되도록 한다.
1. 요청 ID 생성
각 요청마다 고유한 12자리 ID를 생성해 전체 요청 흐름을 추적할 수 있게 한다.
val requestId = UUID.randomUUID().toString().substring(0,12)
MDC.put("request_id", requestId)
2. 사용자 컨텍스트 추출
사용자 컨텍스트를 추출해 넣어준다.
val adminId = extractAdminId(wrappedRequest)
val user = extractUserInfo(wrappedRequest)
user?.let { MDC.put("user_id", it.id.toString()) }
3. 메모리 관리
메모리 누수를 방지지하고 다음 요청과의 격리를 보장하기 위해 요청 처리 완료 후 반드시 MDC를 정리해주어야 한다.
try {
chain.doFilter(wrappedRequest, response)
} finally {
MDC.clear()
}
4. HTTP Body 중복 읽기 문제 해결
문제 상황
HttpServletRequest의 InputStream은 기본적으로 한 번만 읽을 수 있다.
클라이언트 → 네트워크 → Servlet Container → HttpServletRequest
↓
[InputStream Buffer]
↓
한 번만 읽기 가능
따라서 필터에서 body를 읽으면 컨트롤러에서 빈 데이터를 받게 되는 문제가 발생한다.
해결 방안 : CachedBodyHttpServletRequest
HTTP 요청의 body를 메모리에 캐싱하여 여러 번 읽을 수 있게 하는 래퍼 클래스를 만들었다.
class CachedBodyHttpServletRequest(request: HttpServletRequest) : HttpServletRequestWrapper(request) {
private val cachedBody: ByteArray = request.inputStream.readAllBytes()
override fun getInputStream(): ServletInputStream {
val byteStream = ByteArrayInputStream(cachedBody)
return object : ServletInputStream() {
override fun read(): Int = byteStream.read()
override fun isFinished() = byteStream.available() == 0
override fun isReady() = true
override fun setReadListener(listener: ReadListener?) {}
}
}
fun getCachedBodyString(): String = String(cachedBody, StandardCharsets.UTF_8)
}
'study > Server' 카테고리의 다른 글
| [Database] 데이터베이스 Master-Slave 구조란? (0) | 2024.11.04 |
|---|---|
| [Gradle] 스프링부트 빌드 시 plain.jar vs .jar 차이점 (0) | 2024.09.24 |
| Caddy로 도메인 없이 https 설정하기 (1) | 2024.09.07 |
| HTTP 응답 헤더, Content-Disposition (0) | 2024.08.01 |
| [error] Node.js Error <SyntaxError: Cannot use import statement outside a module> (0) | 2023.11.20 |