[#43] feat: request, response 내용 logging 적용해보기
- request, response 내용 로깅을 위한 filter 적용 - request, response 관련 캐싱 기능 적용 - json converting을 위한 Gson library 적용
This commit is contained in:
@@ -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")
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user