100 lines
3.5 KiB
Kotlin

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<String, Any> = 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<String, Any> {
val signature = joinPoint.signature as MethodSignature
val args = joinPoint.args
val payload = mutableMapOf<String, Any>()
payload["controller_method"] = joinPoint.signature.toShortString()
val requestParams: MutableMap<String, Any> = mutableMapOf()
val pathVariables: MutableMap<String, Any> = 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
}
}