diff --git a/src/main/kotlin/roomescape/common/log/LogConfiguration.kt b/src/main/kotlin/roomescape/common/log/LogConfiguration.kt new file mode 100644 index 00000000..5905b7c6 --- /dev/null +++ b/src/main/kotlin/roomescape/common/log/LogConfiguration.kt @@ -0,0 +1,35 @@ +package roomescape.common.log + +import com.fasterxml.jackson.databind.ObjectMapper +import org.springframework.boot.web.servlet.FilterRegistrationBean +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import org.springframework.context.annotation.DependsOn +import org.springframework.core.Ordered +import org.springframework.web.filter.OncePerRequestFilter + +@Configuration +class LogConfiguration { + + @Bean + @DependsOn(value = ["apiLogMessageConverter"]) + fun filterRegistrationBean( + apiLogMessageConverter: ApiLogMessageConverter + ): FilterRegistrationBean { + val filter = HttpRequestLoggingFilter(apiLogMessageConverter) + + return FilterRegistrationBean(filter) + .apply { this.order = Ordered.HIGHEST_PRECEDENCE + 2 } + } + + @Bean + @DependsOn(value = ["apiLogMessageConverter"]) + fun apiLoggingAspect(apiLogMessageConverter: ApiLogMessageConverter): ControllerLoggingAspect { + return ControllerLoggingAspect(apiLogMessageConverter) + } + + @Bean + fun apiLogMessageConverter(objectMapper: ObjectMapper): ApiLogMessageConverter { + return ApiLogMessageConverter(objectMapper) + } +} diff --git a/src/main/kotlin/roomescape/common/log/LoggingFilter.kt b/src/main/kotlin/roomescape/common/log/LoggingFilter.kt deleted file mode 100644 index 07a63658..00000000 --- a/src/main/kotlin/roomescape/common/log/LoggingFilter.kt +++ /dev/null @@ -1,74 +0,0 @@ -package roomescape.common.log - -import com.fasterxml.jackson.databind.ObjectMapper -import io.github.oshai.kotlinlogging.KotlinLogging -import jakarta.servlet.FilterChain -import jakarta.servlet.http.HttpServletRequest -import jakarta.servlet.http.HttpServletResponse -import org.springframework.core.Ordered -import org.springframework.core.annotation.Order -import org.springframework.stereotype.Component -import org.springframework.web.filter.OncePerRequestFilter -import org.springframework.web.util.ContentCachingRequestWrapper -import org.springframework.web.util.ContentCachingResponseWrapper -import java.nio.charset.StandardCharsets - -private val log = KotlinLogging.logger {} - -@Component -@Order(Ordered.HIGHEST_PRECEDENCE) -class LoggingFilter( - private val objectMapper: ObjectMapper -) : OncePerRequestFilter() { - - override fun doFilterInternal( - request: HttpServletRequest, - response: HttpServletResponse, - filterChain: FilterChain - ) { - val cachedRequest = ContentCachingRequestWrapper(request) - val cachedResponse = ContentCachingResponseWrapper(response) - - val startTime = System.currentTimeMillis() - filterChain.doFilter(cachedRequest, cachedResponse) - val duration = System.currentTimeMillis() - startTime - - logAPISummary(cachedRequest, cachedResponse, duration) - cachedResponse.copyBodyToResponse() - - } - - private fun logAPISummary( - request: ContentCachingRequestWrapper, - response: ContentCachingResponseWrapper, - duration: Long - ) { - val payload = linkedMapOf( - "type" to "API_LOG", - "method" to request.method, - "url" to request.requestURL.toString(), - ) - request.queryString?.let { payload["query_params"] = it } - payload["remote_ip"] = request.remoteAddr - payload["status_code"] = response.status - payload["duration_ms"] = duration - - if (log.isDebugEnabled()) { - request.contentAsByteArray.takeIf { it.isNotEmpty() } - ?.let { payload["request_body"] = parseContent(it) } - - response.contentAsByteArray.takeIf { it.isNotEmpty() } - ?.let { payload["response_body"] = parseContent(it) } - } - - log.info { objectMapper.writeValueAsString(payload) } - } - - private fun parseContent(content: ByteArray): Any { - return try { - objectMapper.readValue(content, Map::class.java) - } catch (_: Exception) { - String(content, StandardCharsets.UTF_8) - } - } -}