개발
home
✍🏻

Spring에서 HTTP 요청 로깅하기

Created
2024/03/29
Tags
SpringBoot 3
HTTP
logging
2024-03-29 @이영훈

개요

시스템을 운영 시 API 모니터링과 디버깅을 위해서 HTTP 요청을 로깅하는 것이 중요합니다.
Spring에서 CommonsRequestLoggingFilter 를 사용하거나 HandlerInterceptor 를 구현하여 로직을 구현할 수 있습니다.
저는 Filter를 이용하여 구현하였습니다. Interceptor가 더 유연(더 많은 것을 지원)하지만 Filter에서 보여주는 데이터로 충분했고 빈으로 등록하는 방식이 제가 생각하기에 더 예쁜 코드라 생각했습니다.

CommonsRequestLoggingFilter 방법

CommonsRequestLoggingFilter를 사용하여 로그를 출력하려면 몇 가지 사항을 확인해야 합니다.
로그 레벨: CommonsRequestLoggingFilter는 기본적으로 DEBUG 레벨에서 로그를 출력합니다. 따라서 로그 설정에서 DEBUG 레벨이 활성화되어 있는지 확인해야 합니다. application.yml 에서 로그 레벨을 설정할 수 있습니다.
필터 순서: CommonsRequestLoggingFilter는 필터 체인에서 가능한 한 먼저 실행되어야 합니다. 그렇지 않으면 다른 필터에 의해 요청이 거부되거나 변경될 수 있습니다. 필터의 순서를 설정하려면 FilterRegistrationBean을 사용할 수 있습니다.
이를 구현한 코드입니다:
import org.springframework.boot.web.servlet.FilterRegistrationBean import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration import org.springframework.core.Ordered import org.springframework.web.filter.CommonsRequestLoggingFilter @Configuration class RequestLoggingFilterConfig { @Bean fun logFilter(): FilterRegistrationBean<CommonsRequestLoggingFilter> { val filter = CommonsRequestLoggingFilter() filter.setIncludeQueryString(true) filter.setIncludePayload(true) filter.setMaxPayloadLength(10000) filter.setIncludeHeaders(false) filter.setAfterMessagePrefix("REQUEST DATA : ") val registrationBean = FilterRegistrationBean<CommonsRequestLoggingFilter>() registrationBean.filter = filter registrationBean.order = Ordered.HIGHEST_PRECEDENCE // Set the order to the highest precedence return registrationBean } }
Kotlin
복사
# application.yml logging: level: org.springframework.web.filter.CommonsRequestLoggingFilter: DEBUG
YAML
복사
로그 예시
2024-03-29T15:00:07.744+09:00 DEBUG 59495 --- [nio-8080-exec-2] o.s.w.f.CommonsRequestLoggingFilter : Before request [POST /v1/login] // 생략... 2024-03-29T15:00:08.180+09:00 DEBUG 59495 --- [nio-8080-exec-2] o.s.w.f.CommonsRequestLoggingFilter : REQUEST DATA : POST /v1/login, payload={ "email": "test@email.com", "password": "mysecretpassword" }]
JSON
복사

Interceptor를 사용하는 방법

interceptor를 사용하는 방법은 더 유연하게(더 많은 것을 할 수 있음) 기록할 수 있습니다
HandlerInterceptor는 Spring MVC의 컨트롤러를 실행하기 전, 후, 그리고 완료 시점에 특정 작업을 수행하도록 설계되었습니다.
다음은 HandlerInterceptor를 사용하여 요청과 응답을 로깅하는 방법입니다:
1.
HandlerInterceptor 인터페이스를 구현하는 클래스를 생성합니다. 이 클래스에서는 preHandle, postHandle, afterCompletion 메서드를 오버라이드하여 요청과 응답을 로깅합니다.
2.
이 인터셉터를 Spring MVC의 인터셉터 목록에 등록합니다. 이를 위해 WebMvcConfigurer 인터페이스를 구현하는 설정 클래스를 생성하고, addInterceptors 메서드를 오버라이드하여 인터셉터를 등록합니다.
이를 구현한 코드입니다:
참고로, 저는 KotlinLogging을 이용하여 logger을 하였습니다
import jakarta.servlet.http.HttpServletRequest import jakarta.servlet.http.HttpServletResponse import mu.KotlinLogging // https://github.com/oshai/kotlin-logging import org.springframework.stereotype.Component import org.springframework.web.servlet.HandlerInterceptor @Component class LoggingInterceptor : HandlerInterceptor { private val logger = KotlinLogging.logger {} override fun preHandle(request: HttpServletRequest, response: HttpServletResponse, handler: Any): Boolean { // 요청 로깅 로직 val cachingRequest = ContentCachingRequestWrapper(request) val payload = cachingRequest.contentAsByteArray.toString(Charsets.UTF_8) logger.debug("REQUEST DATA : method=${cachingRequest.method}, uri=${cachingRequest.requestURI}, query=${cachingRequest.queryString}, payload=$payload") return true } override fun afterCompletion( request: HttpServletRequest, response: HttpServletResponse, handler: Any, ex: Exception?, ) { // 요청 처리 완료 후 로직 logger.info { "Response status: ${response.status}" } } }
Kotlin
복사
import org.springframework.context.annotation.Configuration import org.springframework.web.servlet.config.annotation.InterceptorRegistry import org.springframework.web.servlet.config.annotation.WebMvcConfigurer @Configuration class WebConfig( private val loggingInterceptor: LoggingInterceptor ) : WebMvcConfigurer { override fun addInterceptors(registry: InterceptorRegistry) { registry.addInterceptor(loggingInterceptor) } }
Kotlin
복사