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 메서드를 오버라이드하여 인터셉터를 등록합니다.
이를 구현한 코드입니다:
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
복사