package roomescape.common.log import io.github.oshai.kotlinlogging.KLogger import io.github.oshai.kotlinlogging.KotlinLogging import jakarta.servlet.http.HttpServletRequest import org.aspectj.lang.JoinPoint import org.aspectj.lang.ProceedingJoinPoint 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 import org.springframework.web.bind.annotation.RequestParam import org.springframework.web.context.request.RequestContextHolder import org.springframework.web.context.request.ServletRequestAttributes private val log: KLogger = KotlinLogging.logger {} @Aspect class ControllerLoggingAspect( private val messageConverter: ApiLogMessageConverter, ) { @Pointcut("execution(* roomescape..web..*Controller.*(..))") fun allController() { } @Around("allController()") fun logAPICalls(joinPoint: ProceedingJoinPoint): Any? { val startTime: Long = MDC.get("startTime").toLongOrNull() ?: System.currentTimeMillis() val controllerPayload: Map = parsePayload(joinPoint) log.info { messageConverter.convertToControllerInvokedMessage(servletRequest(), controllerPayload) } try { return joinPoint.proceed() .also { logSuccess(startTime, it) } } catch (e: Exception) { throw e } } private fun logSuccess(startTime: Long, result: Any) { val responseEntity = result as ResponseEntity<*> var convertResponseMessageRequest = ConvertResponseMessageRequest( type = LogType.CONTROLLER_SUCCESS, httpStatus = responseEntity.statusCode.value(), startTime = startTime, ) if (log.isDebugEnabled()) { convertResponseMessageRequest = convertResponseMessageRequest.copy( body = responseEntity.body ) } val logMessage = messageConverter.convertToResponseMessage(convertResponseMessageRequest) log.info { logMessage } } private fun servletRequest(): HttpServletRequest { return (RequestContextHolder.currentRequestAttributes() as ServletRequestAttributes).request } private fun parsePayload(joinPoint: JoinPoint): Map { val signature = joinPoint.signature as MethodSignature val args = joinPoint.args val payload = mutableMapOf() payload["controller_method"] = joinPoint.signature.toShortString() val requestParams: MutableMap = mutableMapOf() val pathVariables: MutableMap = mutableMapOf() signature.method.parameters.forEachIndexed { index, parameter -> val arg = args[index] parameter.getAnnotation(RequestBody::class.java)?.let { payload["request_body"] = arg } parameter.getAnnotation(PathVariable::class.java)?.let { pathVariables[parameter.name] = arg } parameter.getAnnotation(RequestParam::class.java)?.let { requestParams[parameter.name] = arg } } if (pathVariables.isNotEmpty()) payload["path_variable"] = pathVariables if (requestParams.isNotEmpty()) payload["request_param"] = requestParams return payload } }