From 90063e290399eb3a08efa909fc673ac1d364e9a5 Mon Sep 17 00:00:00 2001 From: Jeyong Date: Tue, 2 Dec 2025 22:58:35 +0900 Subject: [PATCH 1/3] =?UTF-8?q?feat:=20=EC=82=AC=EC=9A=A9=EC=9E=90=20Reque?= =?UTF-8?q?st/Response=EC=97=90=20=EB=8C=80=ED=95=9C=20=EB=A1=9C=EA=B9=85?= =?UTF-8?q?=20=EC=84=A4=EC=A0=95=20=EB=B0=8F=20=EB=A7=88=EC=8A=A4=ED=82=B9?= =?UTF-8?q?=20=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../authserver/global/aop/LogAspect.java | 116 ++++++++++++++++++ .../authserver/global/util/MaskingUtil.java | 96 +++++++++++++++ 2 files changed, 212 insertions(+) create mode 100644 src/main/java/org/creditto/authserver/global/aop/LogAspect.java create mode 100644 src/main/java/org/creditto/authserver/global/util/MaskingUtil.java diff --git a/src/main/java/org/creditto/authserver/global/aop/LogAspect.java b/src/main/java/org/creditto/authserver/global/aop/LogAspect.java new file mode 100644 index 0000000..108cff0 --- /dev/null +++ b/src/main/java/org/creditto/authserver/global/aop/LogAspect.java @@ -0,0 +1,116 @@ +package org.creditto.authserver.global.aop; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +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.Before; +import org.aspectj.lang.annotation.Pointcut; +import org.creditto.authserver.global.response.BaseResponse; +import org.creditto.authserver.global.util.MaskingUtil; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Component; +import org.springframework.validation.BindingResult; +import org.springframework.web.context.request.RequestAttributes; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +import java.util.Arrays; + +@Aspect +@Slf4j +@Component +@RequiredArgsConstructor +public class LogAspect { + + private final ObjectMapper objectMapper; + + @Pointcut("execution(* org.creditto.authserver..controller..*(..))") + private void onRequest() { + } + + @Pointcut("execution(* org.creditto.authserver..service..*(..))") + private void onService() { + } + + @Around("onRequest()") + public Object logRequestAndResponse(final ProceedingJoinPoint joinPoint) throws Throwable { + final ServletRequestAttributes requestAttributes = getRequestAttributes(); + final HttpServletRequest request = requestAttributes != null ? requestAttributes.getRequest() : null; + + final String className = joinPoint.getTarget().getClass().getSimpleName(); + final String requestUri = request != null ? request.getRequestURI() : joinPoint.getSignature().toShortString(); + final String method = request != null ? request.getMethod() : "N/A"; + final String requestIp = request != null ? request.getRemoteAddr() : "N/A"; + + log.info("[{}] Request IP : {} | Request URI : {} | Request Method : {}", className, requestIp, requestUri, method); + + final Object[] sanitizedArgs = Arrays.stream(joinPoint.getArgs()) + .filter(arg -> !(arg instanceof HttpServletRequest)) + .filter(arg -> !(arg instanceof HttpServletResponse)) + .filter(arg -> !(arg instanceof BindingResult)) + .toArray(); + + if (sanitizedArgs.length > 0) { + final String argsAsString = MaskingUtil.maskSensitiveData(serialize(sanitizedArgs)); + log.info("[{}] {} {} - RequestBody: {}", className, method, requestUri, argsAsString); + } + + final Object result = joinPoint.proceed(); + + Object responseBody = result; + if (result instanceof ResponseEntity responseEntity) { + responseBody = responseEntity.getBody(); + } + + final Object dataForLog = responseBody instanceof BaseResponse baseResponse + ? baseResponse.getData() + : responseBody; + + final String responseAsString = MaskingUtil.maskSensitiveData(serialize(dataForLog)); + log.info("[{}] {} {} - ResponseData: {}", className, method, requestUri, responseAsString); + + return result; + } + + @Before("onService()") + public void beforeServiceLog(final JoinPoint joinPoint) { + final String className = joinPoint.getTarget().getClass().getSimpleName(); + final String methodName = joinPoint.getSignature().getName(); + final Object[] args = joinPoint.getArgs(); + + log.info("[{}] {}() called", className, methodName); + + if (args.length > 0) { + final String params = MaskingUtil.maskSensitiveData(serialize(args)); + log.debug("[{}] Parameters: {}", className, params); + } + } + + private ServletRequestAttributes getRequestAttributes() { + final RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes(); + if (requestAttributes instanceof ServletRequestAttributes servletRequestAttributes) { + return servletRequestAttributes; + } + return null; + } + + private String serialize(final Object value) { + if (value == null) { + return "null"; + } + + try { + return objectMapper.writeValueAsString(value); + } catch (JsonProcessingException e) { + log.debug("직렬화 실패: {}", e.getMessage()); + return String.valueOf(value); + } + } +} diff --git a/src/main/java/org/creditto/authserver/global/util/MaskingUtil.java b/src/main/java/org/creditto/authserver/global/util/MaskingUtil.java new file mode 100644 index 0000000..2bc2865 --- /dev/null +++ b/src/main/java/org/creditto/authserver/global/util/MaskingUtil.java @@ -0,0 +1,96 @@ +package org.creditto.authserver.global.util; + +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public final class MaskingUtil { + + private static final List SENSITIVE_KEYS = List.of( + "password", + "secret", + "token", + "key", + "certificateNumber" + ); + + private static final Pattern JSON_KEY_VALUE_PATTERN = Pattern.compile( + "(\"(?:" + String.join("|", SENSITIVE_KEYS) + ")\"\\s*:\\s*\")(.*?)(\")", + Pattern.CASE_INSENSITIVE + ); + + private static final Pattern KEY_VALUE_PATTERN = Pattern.compile( + "(?i)((?:password|secret|token|key[number]?|certificateNumber)\\s*[=:]\\s*)([^,\\s}\\]]+)" + ); + + private static final Pattern PHONE_PATTERN = Pattern.compile("(? Date: Tue, 2 Dec 2025 22:58:49 +0900 Subject: [PATCH 2/3] =?UTF-8?q?ci:=20=ED=97=AC=EC=8A=A4=EC=B2=B4=ED=81=AC?= =?UTF-8?q?=20=EC=9E=A1=20=EB=8C=80=EA=B8=B0=EC=8B=9C=EA=B0=84=20=EC=A6=9D?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/CD.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/CD.yml b/.github/workflows/CD.yml index e28602e..9ec8a01 100644 --- a/.github/workflows/CD.yml +++ b/.github/workflows/CD.yml @@ -138,7 +138,7 @@ jobs: username: ${{ secrets.EC2_USER }} key: ${{ secrets.EC2_KEY }} script: | - sleep 5; + sleep 10; echo "Health Check 시작...🩺" for i in {1..10} @@ -150,7 +150,7 @@ jobs: fi echo "⏳ 서버 기동 대기중... ($i/10)" - sleep 5 + sleep 10 done echo "❌ Health Check 실패" From d73db48cb58d1ff856f0f6a0a124d9234cdd5f78 Mon Sep 17 00:00:00 2001 From: Jeyong Date: Tue, 2 Dec 2025 23:06:23 +0900 Subject: [PATCH 3/3] =?UTF-8?q?refactor:=20PR=20Review=20=EB=B0=98?= =?UTF-8?q?=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../authserver/global/aop/LogAspect.java | 29 ++++++++++--------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/src/main/java/org/creditto/authserver/global/aop/LogAspect.java b/src/main/java/org/creditto/authserver/global/aop/LogAspect.java index 108cff0..773e57c 100644 --- a/src/main/java/org/creditto/authserver/global/aop/LogAspect.java +++ b/src/main/java/org/creditto/authserver/global/aop/LogAspect.java @@ -62,21 +62,22 @@ public Object logRequestAndResponse(final ProceedingJoinPoint joinPoint) throws log.info("[{}] {} {} - RequestBody: {}", className, method, requestUri, argsAsString); } - final Object result = joinPoint.proceed(); - - Object responseBody = result; - if (result instanceof ResponseEntity responseEntity) { - responseBody = responseEntity.getBody(); + try { + final Object result = joinPoint.proceed(); + Object responseBody = result; + if (result instanceof ResponseEntity responseEntity) { + responseBody = responseEntity.getBody(); + } + final Object dataForLog = responseBody instanceof BaseResponse baseResponse + ? baseResponse.getData() + : responseBody; + final String responseAsString = MaskingUtil.maskSensitiveData(serialize(dataForLog)); + log.info("[{}] {} {} - ResponseData: {}", className, method, requestUri, responseAsString); + return result; + } catch (Throwable e) { + log.error("[{}] {} {} - Exception occurred : {}", className, method, requestUri, e.getMessage()); + throw e; } - - final Object dataForLog = responseBody instanceof BaseResponse baseResponse - ? baseResponse.getData() - : responseBody; - - final String responseAsString = MaskingUtil.maskSensitiveData(serialize(dataForLog)); - log.info("[{}] {} {} - ResponseData: {}", className, method, requestUri, responseAsString); - - return result; } @Before("onService()")