Spring Boot에서 HTTP 요청/응답 로깅 필터 구현하기

배경

서비스를 운영하면서 디버깅 시 어려움을 겪었습니다.

 

특히 예외가 발생하지 않았지만 오류인 경우를 파악하기 어려웠습니다. 예를 들어, 사용자는 A 옵션을 선택했다고 생각했으나 클라이언트 앱에서 잘못 처리되어 B로 전송되는 경우가 있었습니다. 이런 상황에서는 서버 입장에서 정상적으로 처리되기 때문에 예외 로그가 남지 않지만, 실제로는 사용자 의도와 다른 결과가 발생하는 문제였습니다.

 


기존에는 사용자가 어떤 요청을 했는지 로그 포맷이 따로 정해져 있지 않아 필요한 시점에 개별적으로 로그를 추가하는 방식으로 작업했기 때문에 다음과 같은 문제점이 있었습니다.

  • API 요청/응답 흐름을 추적하기 어려움
  • 일관되지 않은 로그 형식
  • 문제 발생 시 원인 파악에 시간이 오래 걸림
  • 새로운 로그가 필요하다면 재배포가 필요함

 

해결 방법

HTTP 요청의 특성과 문제점

HTTP 요청의 InputStream은 한 번만 읽을 수 있습니다.

따라서 필터에서 body를 읽어버리면 실제 컨트롤러에서는 해당 내용을 읽을 수 없게 됩니다.

 

ContentCachingWrapper 활용

Spring에서 제공하는 다음 클래스들을 사용하면 이 문제를 해결할 수 있습니다.

ContentCachingRequestWrapper
ContentCachingResponseWrapper

 

이 Wrapper들은 요청/응답의 내용을 내부 버퍼에 캐싱하여, 여러 번 읽을 수 있도록 해줍니다.

 

LoggingFilter 구현

모든 API 요청이 LoggingFilter를 거치도록 설정하고, Wrapper를 활용하여 다음 정보들을 로그로 남겼습니다.

  • HTTP Method
  • 요청 URL
  • 요청 Body
  • 응답 Body
  • HTTP 상태 코드
  • API 실행 시간

 

구현 결과

로그 예시

[INFO] [GET] /api/v1/products - Status: 200 - 45ms
Request: {"language": "ko"}
Response: {"products": [...]}

장점

  1. 일관된 로그 형식: 모든 API에 대해 동일한 형식으로 로깅
  2. 디버깅 효율성 향상: 요청/응답을 한눈에 확인 가능
  3. CloudWatch 활용: 로그 검색 및 필터링이 용이해짐
  4. 성능 모니터링: API별 실행 시간 추적 가능
  5. 유저별 모니터링: API별 어떤 사용자가 어떤 요청을 했는지 추적 가능

LoggingFilter만 INFO 레벨 유지하고

다른 로거들은 WARN 또는 ERROR 레벨로 상향 조정한다면 불필요한 로그 출력 최소화로 성능 개선도 할 수 있을 것입니다.

 

마치며

간단한 필터 구현만으로도 디버깅과 모니터링이 훨씬 수월해졌습니다.

 

이제는 클라이언트-서버 간 데이터 불일치나 예상치 못한 동작을 로그만으로도 빠르게 파악할 수 있어

실제 서비스 환경에서의 이슈 대응 시간이 크게 단축되었습니다.

 

또한 예전에는 이슈 파악을 위해 여러 곳에 임시 로그를 추가하고 재배포해야 했다면, 
이제는 CloudWatch에서 요청/응답을 바로 확인하며 문제를 해결할 수 있게 되었습니다.

 

하나의 개선만으로 많은 것들을 편리하게 디버깅 할 수 있게 되어 매우 만족한 개선 중 하나입니다 👍