[#43] feat: request, response 내용 logging 적용해보기

- request, response 내용 로깅을 위한 filter 적용
- request, response 관련 캐싱 기능 적용
- json converting을 위한 Gson library 적용
This commit is contained in:
beaniejoy
2023-04-14 02:04:48 +09:00
parent 8107468562
commit a108cdd466
4 changed files with 87 additions and 36 deletions

View File

@@ -67,6 +67,8 @@ subprojects {
// Logging
implementation("io.github.microutils:kotlin-logging:${Version.Deps.KOTLIN_LOGGING}")
implementation("com.google.code.gson:gson")
// Test
testImplementation("org.springframework.boot:spring-boot-starter-test")

View File

@@ -1,32 +0,0 @@
package io.beaniejoy.dongnecafe.filter
import mu.KotlinLogging
import org.slf4j.MDC
import org.springframework.core.annotation.Order
import org.springframework.stereotype.Component
import org.springframework.web.filter.OncePerRequestFilter
import java.util.UUID
import javax.servlet.FilterChain
import javax.servlet.http.HttpServletRequest
import javax.servlet.http.HttpServletResponse
// TODO: 리팩토링 필요(logback-spring.xml 같이)
@Component
@Order(1)
class LoggingFilter: OncePerRequestFilter() {
private val log = KotlinLogging.logger {}
override fun doFilterInternal(
request: HttpServletRequest,
response: HttpServletResponse,
filterChain: FilterChain,
) {
val requestId = UUID.randomUUID().toString().substring(0, 8)
MDC.put("request_id", requestId)
log.info{ "request_id = $requestId" }
filterChain.doFilter(request, response)
MDC.remove("request_id")
}
}

View File

@@ -0,0 +1,80 @@
package io.beaniejoy.dongnecafe.infra
import com.google.gson.Gson
import mu.KotlinLogging
import org.slf4j.MDC
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.util.UUID
import javax.servlet.FilterChain
import javax.servlet.http.HttpServletRequest
import javax.servlet.http.HttpServletResponse
// TODO: 리팩토링 필요(logback-spring.xml 같이)
@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
class RequestResponseLoggingFilter : OncePerRequestFilter() {
private val log = KotlinLogging.logger {}
companion object {
const val REQUEST_ID = "request_id"
}
override fun doFilterInternal(
request: HttpServletRequest,
response: HttpServletResponse,
filterChain: FilterChain,
) {
val cachingRequestWrapper = ContentCachingRequestWrapper(request)
val cachingResponseWrapper = ContentCachingResponseWrapper(response)
val requestId = UUID.randomUUID().toString().substring(0, 8)
MDC.put(REQUEST_ID, requestId)
val startTime = System.currentTimeMillis()
filterChain.doFilter(cachingRequestWrapper, cachingResponseWrapper)
val end = System.currentTimeMillis()
// TODO: GET에 대해서 request body가 안찍힘
log.info { """
|
|[REQUEST] ${request.method} ${request.requestURI} ${response.status} (${(end - startTime) / 1000.0})
|HEADERS: ${getRequestHeaders(request)}
|REQUEST_PARAM: ${getRequestParams(request)}
|REQUEST_BODY: ${getRequestBody(cachingRequestWrapper)}
|RESPONSE_BODY: ${getResponseBody(cachingResponseWrapper)}
""".trimMargin() }
cachingResponseWrapper.copyBodyToResponse()
MDC.remove(REQUEST_ID)
}
private fun getRequestHeaders(request: HttpServletRequest): String {
return Gson().toJson(
mutableMapOf<String, String?>().apply {
request.headerNames.toList().forEach { this[it] = request.getHeader(it) }
}
)
}
private fun getRequestParams(request: HttpServletRequest): String {
return request.parameterMap.mapValues {
it.value.joinToString(",")
}.entries.joinToString("&")
}
private fun getRequestBody(requestWrapper: ContentCachingRequestWrapper): String {
return requestWrapper.contentAsByteArray.toString(Charsets.UTF_8)
}
// TODO: logging response body maximum size 고려
private fun getResponseBody(responseWrapper: ContentCachingResponseWrapper): String {
return responseWrapper.contentAsByteArray.toString(Charsets.UTF_8)
}
}

View File

@@ -3,10 +3,10 @@
<include resource="org/springframework/boot/logging/logback/defaults.xml"/>
<include resource="org/springframework/boot/logging/logback/console-appender.xml"/>
<!-- pattern -->
<!-- Pattern -->
<property name="LOG_PATTERN" value="%d{yyyy-MM-dd HH:mm:ss.SSS} %clr(%5level) [%15.15t] [%X{request_id}] %clr(%-40.40logger{39}){cyan} : %m%n%wEx"/>
<!-- Console Appender -->
<appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
<!-- Request Thread Console Appender -->
<appender name="THREAD_CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>${LOG_PATTERN}</pattern>
</encoder>
@@ -14,9 +14,10 @@
<springProfile name="local">
<logger additivity="false" level="INFO" name="io.beaniejoy.dongnecafe">
<appender-ref ref="stdout"/>
<appender-ref ref="THREAD_CONSOLE"/>
</logger>
<!-- Bootstrap class file -->
<logger additivity="false" level="INFO" name="io.beaniejoy.dongnecafe.DongneServiceApiApplicationKt">
<appender-ref ref="CONSOLE"/>
</logger>