diff --git a/common/types/src/main/kotlin/com/sangdol/common/types/web/HttpStatus.kt b/common/types/src/main/kotlin/com/sangdol/common/types/web/HttpStatus.kt index 387cfed8..35764d15 100644 --- a/common/types/src/main/kotlin/com/sangdol/common/types/web/HttpStatus.kt +++ b/common/types/src/main/kotlin/com/sangdol/common/types/web/HttpStatus.kt @@ -14,6 +14,10 @@ enum class HttpStatus( INTERNAL_SERVER_ERROR(500) ; + fun isClientError(): Boolean { + return code in 400..<500 + } + fun value(): Int { return code } diff --git a/common/log/src/main/kotlin/com/sangdol/common/log/web/ControllerLoggingAspect.kt b/common/web/src/main/kotlin/com/sangdol/common/web/asepct/ControllerLoggingAspect.kt similarity index 61% rename from common/log/src/main/kotlin/com/sangdol/common/log/web/ControllerLoggingAspect.kt rename to common/web/src/main/kotlin/com/sangdol/common/web/asepct/ControllerLoggingAspect.kt index 1b10df4c..68d87803 100644 --- a/common/log/src/main/kotlin/com/sangdol/common/log/web/ControllerLoggingAspect.kt +++ b/common/web/src/main/kotlin/com/sangdol/common/web/asepct/ControllerLoggingAspect.kt @@ -1,9 +1,7 @@ -package com.sangdol.common.log.web +package com.sangdol.common.web.asepct -import com.sangdol.common.log.config.LogType -import com.sangdol.common.log.message.ApiLogMessageConverter -import com.sangdol.common.log.message.ConvertResponseMessageRequest -import com.sangdol.common.log.message.getEndpoint +import com.sangdol.common.log.constant.LogType +import com.sangdol.common.web.support.log.WebLogMessageConverter import io.github.oshai.kotlinlogging.KLogger import io.github.oshai.kotlinlogging.KotlinLogging import jakarta.servlet.http.HttpServletRequest @@ -13,7 +11,6 @@ import org.aspectj.lang.annotation.Around import org.aspectj.lang.annotation.Aspect import org.aspectj.lang.annotation.Pointcut import org.aspectj.lang.reflect.MethodSignature -import org.slf4j.MDC import org.springframework.http.ResponseEntity import org.springframework.web.bind.annotation.PathVariable import org.springframework.web.bind.annotation.RequestBody @@ -25,7 +22,7 @@ private val log: KLogger = KotlinLogging.logger {} @Aspect class ControllerLoggingAspect( - private val messageConverter: ApiLogMessageConverter, + private val messageConverter: WebLogMessageConverter, ) { @Pointcut("execution(* com.sangdol..web..*Controller*.*(..))") @@ -34,10 +31,8 @@ class ControllerLoggingAspect( @Around("allController()") fun logAPICalls(joinPoint: ProceedingJoinPoint): Any? { - val startTime: Long = MDC.get("startTime").toLongOrNull() ?: System.currentTimeMillis() - val controllerPayload: Map = parsePayload(joinPoint) - val servletRequest: HttpServletRequest = servletRequest() + val controllerPayload: Map = parseControllerPayload(joinPoint) log.info { messageConverter.convertToControllerInvokedMessage(servletRequest, controllerPayload) @@ -45,29 +40,22 @@ class ControllerLoggingAspect( try { return joinPoint.proceed() - .also { logSuccess(servletRequest.getEndpoint(), startTime, it) } + .also { logSuccess(servletRequest, it as ResponseEntity<*>) } } catch (e: Exception) { throw e } } - private fun logSuccess(endpoint: String, startTime: Long, result: Any) { - val responseEntity = result as ResponseEntity<*> - var convertResponseMessageRequest = ConvertResponseMessageRequest( - type = LogType.CONTROLLER_SUCCESS, - endpoint = endpoint, - httpStatus = responseEntity.statusCode.value(), - startTime = startTime, + private fun logSuccess(servletRequest: HttpServletRequest, result: ResponseEntity<*>) { + val body: Any? = if (log.isDebugEnabled()) result.body else null + + val logMessage = messageConverter.convertToResponseMessage( + type = LogType.SUCCEED, + servletRequest = servletRequest, + httpStatusCode = result.statusCode.value(), + responseBody = body, ) - if (log.isDebugEnabled()) { - convertResponseMessageRequest = convertResponseMessageRequest.copy( - body = responseEntity.body - ) - } - - val logMessage = messageConverter.convertToResponseMessage(convertResponseMessageRequest) - log.info { logMessage } } @@ -75,14 +63,16 @@ class ControllerLoggingAspect( return (RequestContextHolder.currentRequestAttributes() as ServletRequestAttributes).request } - private fun parsePayload(joinPoint: JoinPoint): Map { + private fun parseControllerPayload(joinPoint: JoinPoint): Map { val signature = joinPoint.signature as MethodSignature val args = joinPoint.args - val payload = mutableMapOf() - payload["controller_method"] = joinPoint.signature.toShortString() + val payload = mutableMapOf( + "controller_method" to joinPoint.signature.toShortString() + ) val requestParams: MutableMap = mutableMapOf() val pathVariables: MutableMap = mutableMapOf() + signature.method.parameters.forEachIndexed { index, parameter -> val arg = args[index] @@ -97,9 +87,10 @@ class ControllerLoggingAspect( parameter.getAnnotation(RequestParam::class.java)?.let { requestParams[parameter.name] = arg } + }.also { + if (pathVariables.isNotEmpty()) payload["path_variable"] = pathVariables + if (requestParams.isNotEmpty()) payload["request_param"] = requestParams } - if (pathVariables.isNotEmpty()) payload["path_variable"] = pathVariables - if (requestParams.isNotEmpty()) payload["request_param"] = requestParams return payload } diff --git a/service/src/main/kotlin/com/sangdol/roomescape/common/exception/ExceptionControllerAdvice.kt b/common/web/src/main/kotlin/com/sangdol/common/web/exception/GlobalExceptionhandler.kt similarity index 59% rename from service/src/main/kotlin/com/sangdol/roomescape/common/exception/ExceptionControllerAdvice.kt rename to common/web/src/main/kotlin/com/sangdol/common/web/exception/GlobalExceptionhandler.kt index 146e74cf..a790a4d2 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/common/exception/ExceptionControllerAdvice.kt +++ b/common/web/src/main/kotlin/com/sangdol/common/web/exception/GlobalExceptionhandler.kt @@ -1,19 +1,15 @@ -package com.sangdol.roomescape.common.exception +package com.sangdol.common.web.exception +import com.sangdol.common.log.constant.LogType import com.sangdol.common.types.exception.CommonErrorCode import com.sangdol.common.types.exception.ErrorCode import com.sangdol.common.types.exception.RoomescapeException import com.sangdol.common.types.web.CommonErrorResponse import com.sangdol.common.types.web.HttpStatus -import com.sangdol.roomescape.auth.exception.AuthException -import com.sangdol.roomescape.common.log.ApiLogMessageConverter -import com.sangdol.roomescape.common.log.ConvertResponseMessageRequest -import com.sangdol.roomescape.common.log.LogType -import com.sangdol.roomescape.common.log.getEndpoint +import com.sangdol.common.web.support.log.WebLogMessageConverter import io.github.oshai.kotlinlogging.KLogger import io.github.oshai.kotlinlogging.KotlinLogging import jakarta.servlet.http.HttpServletRequest -import org.slf4j.MDC import org.springframework.http.ResponseEntity import org.springframework.http.converter.HttpMessageNotReadableException import org.springframework.web.bind.MethodArgumentNotValidException @@ -23,8 +19,8 @@ import org.springframework.web.bind.annotation.RestControllerAdvice private val log: KLogger = KotlinLogging.logger {} @RestControllerAdvice -class ExceptionControllerAdvice( - private val messageConverter: ApiLogMessageConverter +class GlobalExceptionHandler( + private val messageConverter: WebLogMessageConverter ) { @ExceptionHandler(value = [RoomescapeException::class]) fun handleRoomException( @@ -35,14 +31,7 @@ class ExceptionControllerAdvice( val httpStatus: HttpStatus = errorCode.httpStatus val errorResponse = CommonErrorResponse(errorCode) - val type = if (e is AuthException) LogType.AUTHENTICATION_FAILURE else LogType.APPLICATION_FAILURE - logException( - type = type, - servletRequest = servletRequest, - httpStatus = httpStatus.value(), - errorResponse = errorResponse, - exception = e - ) + logException(servletRequest, httpStatus, errorResponse, e) return ResponseEntity .status(httpStatus.value()) @@ -54,26 +43,21 @@ class ExceptionControllerAdvice( servletRequest: HttpServletRequest, e: Exception ): ResponseEntity { - val message: String = if (e is MethodArgumentNotValidException) { + if (e is MethodArgumentNotValidException) { e.bindingResult.allErrors .mapNotNull { it.defaultMessage } .joinToString(", ") } else { e.message!! + }.also { + log.warn { "[ExceptionControllerAdvice] Invalid Request Value Exception occurred: $it" } } - log.debug { "[ExceptionControllerAdvice] Invalid Request Value Exception occurred: $message" } val errorCode: ErrorCode = CommonErrorCode.INVALID_INPUT_VALUE val httpStatus: HttpStatus = errorCode.httpStatus val errorResponse = CommonErrorResponse(errorCode) - logException( - type = LogType.APPLICATION_FAILURE, - servletRequest = servletRequest, - httpStatus = httpStatus.value(), - errorResponse = errorResponse, - exception = e - ) + logException(servletRequest, httpStatus, errorResponse, e) return ResponseEntity .status(httpStatus.value()) @@ -91,13 +75,7 @@ class ExceptionControllerAdvice( val httpStatus: HttpStatus = errorCode.httpStatus val errorResponse = CommonErrorResponse(errorCode) - logException( - type = LogType.UNHANDLED_EXCEPTION, - servletRequest = servletRequest, - httpStatus = httpStatus.value(), - errorResponse = errorResponse, - exception = e - ) + logException(servletRequest, httpStatus, errorResponse, e) return ResponseEntity .status(httpStatus.value()) @@ -105,25 +83,21 @@ class ExceptionControllerAdvice( } private fun logException( - type: LogType, servletRequest: HttpServletRequest, - httpStatus: Int, + httpStatus: HttpStatus, errorResponse: CommonErrorResponse, exception: Exception ) { - val commonRequest = ConvertResponseMessageRequest( - type = type, - endpoint = servletRequest.getEndpoint(), - httpStatus = httpStatus, - startTime = MDC.get("startTime")?.toLongOrNull(), - body = errorResponse, - ) + val type = if (httpStatus.isClientError()) LogType.APPLICATION_FAILURE else LogType.UNHANDLED_EXCEPTION + val actualException: Exception? = if (errorResponse.message == exception.message) null else exception - val logMessage = if (errorResponse.message == exception.message) { - messageConverter.convertToResponseMessage(commonRequest) - } else { - messageConverter.convertToResponseMessage(commonRequest.copy(exception = exception)) - } + val logMessage = messageConverter.convertToResponseMessage( + type = type, + servletRequest = servletRequest, + httpStatusCode = httpStatus.value(), + responseBody = errorResponse, + exception = actualException + ) log.warn { logMessage } } diff --git a/common/log/src/main/kotlin/com/sangdol/common/log/web/HttpRequestLoggingFilter.kt b/common/web/src/main/kotlin/com/sangdol/common/web/servlet/HttpRequestLoggingFilter.kt similarity index 78% rename from common/log/src/main/kotlin/com/sangdol/common/log/web/HttpRequestLoggingFilter.kt rename to common/web/src/main/kotlin/com/sangdol/common/web/servlet/HttpRequestLoggingFilter.kt index eccd372a..a5e45a5d 100644 --- a/common/log/src/main/kotlin/com/sangdol/common/log/web/HttpRequestLoggingFilter.kt +++ b/common/web/src/main/kotlin/com/sangdol/common/web/servlet/HttpRequestLoggingFilter.kt @@ -1,13 +1,13 @@ -package com.sangdol.common.log.web +package com.sangdol.common.web.servlet -import com.sangdol.common.log.message.ApiLogMessageConverter import com.sangdol.common.utils.MdcPrincipalIdUtil +import com.sangdol.common.utils.MdcStartTimeUtil +import com.sangdol.common.web.support.log.WebLogMessageConverter import io.github.oshai.kotlinlogging.KLogger import io.github.oshai.kotlinlogging.KotlinLogging import jakarta.servlet.FilterChain import jakarta.servlet.http.HttpServletRequest import jakarta.servlet.http.HttpServletResponse -import org.slf4j.MDC import org.springframework.web.filter.OncePerRequestFilter import org.springframework.web.util.ContentCachingRequestWrapper import org.springframework.web.util.ContentCachingResponseWrapper @@ -15,7 +15,7 @@ import org.springframework.web.util.ContentCachingResponseWrapper private val log: KLogger = KotlinLogging.logger {} class HttpRequestLoggingFilter( - private val messageConverter: ApiLogMessageConverter + private val messageConverter: WebLogMessageConverter ) : OncePerRequestFilter() { override fun doFilterInternal( request: HttpServletRequest, @@ -27,14 +27,13 @@ class HttpRequestLoggingFilter( val cachedRequest = ContentCachingRequestWrapper(request) val cachedResponse = ContentCachingResponseWrapper(response) - val startTime = System.currentTimeMillis() - MDC.put("startTime", startTime.toString()) + MdcStartTimeUtil.setCurrentTime() try { filterChain.doFilter(cachedRequest, cachedResponse) cachedResponse.copyBodyToResponse() } finally { - MDC.remove("startTime") + MdcStartTimeUtil.clear() MdcPrincipalIdUtil.clear() } }