-
Notifications
You must be signed in to change notification settings - Fork 1
Refactor/252 Logging 설정 추가및 리팩토링 #270
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
74e229c
7b8b957
e2b7eb6
265a3e9
bed7f69
7fa86a9
2cb9f3b
fd484d2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -41,3 +41,6 @@ out/ | |
|
|
||
| ### env file ### | ||
| *.env | ||
|
|
||
| ### log file ### | ||
| /logs/ | ||
This file was deleted.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,90 @@ | ||
| package com.somemore.global.aspect.log; | ||
|
|
||
| import com.somemore.global.aspect.log.extractor.ParameterExtractor; | ||
| import com.somemore.global.aspect.log.extractor.RequestExtractor; | ||
| import com.somemore.global.aspect.log.extractor.ResponseExtractor; | ||
| import com.somemore.global.common.response.LoggedResponse; | ||
| import jakarta.servlet.http.HttpServletRequest; | ||
| import lombok.RequiredArgsConstructor; | ||
| import lombok.extern.slf4j.Slf4j; | ||
| 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.slf4j.MDC; | ||
| import org.springframework.http.HttpStatus; | ||
| import org.springframework.stereotype.Component; | ||
|
|
||
|
|
||
| import java.util.UUID; | ||
|
|
||
| @RequiredArgsConstructor | ||
| @Slf4j | ||
| @Aspect | ||
| @Component | ||
| public class LoggingAspect { | ||
|
|
||
| private final RequestExtractor requestExtractor; | ||
| private final ResponseExtractor responseExtractor; | ||
| private final ParameterExtractor parameterExtractor; | ||
|
|
||
| @Pointcut("execution(* com.somemore.domains.*.controller..*.*(..))") | ||
| private void controllerPointCut() {} | ||
|
|
||
| @Around("controllerPointCut()") | ||
| public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable { | ||
| String requestId = UUID.randomUUID().toString(); | ||
| MDC.put("requestId", requestId); | ||
|
|
||
| try { | ||
| return doLogAround(joinPoint); | ||
| } finally { | ||
| MDC.remove("requestId"); | ||
| } | ||
| } | ||
|
|
||
| private Object doLogAround(ProceedingJoinPoint joinPoint) throws Throwable { | ||
| String methodName = joinPoint.getSignature().toShortString(); | ||
| HttpServletRequest request = requestExtractor.getCurrentRequest(); | ||
|
|
||
| MDC.put("method", request.getMethod()); | ||
| MDC.put("uri", request.getRequestURI()); | ||
|
|
||
| String params = parameterExtractor.extractParameters(joinPoint); | ||
| log.info("엔드포인트 호출: {} \n- URI: {} \n- Method: {} \n- 파라미터: {}", | ||
| methodName, | ||
| request.getRequestURI(), | ||
| request.getMethod(), | ||
| params); | ||
|
|
||
| long startTime = System.currentTimeMillis(); | ||
|
|
||
| try { | ||
| Object result = joinPoint.proceed(); | ||
| long elapsedTime = System.currentTimeMillis() - startTime; | ||
|
|
||
| LoggedResponse loggedResponse = responseExtractor.extractResponse(result); | ||
| log.info("호출 성공: {} \n- 응답 코드: {} \n- 응답 값: {} \n- 실행 시간: {}ms", | ||
| methodName, | ||
| loggedResponse.getStatusCode(), | ||
| loggedResponse.getBody(), | ||
| elapsedTime); | ||
|
|
||
| return result; | ||
| } catch (Exception e) { | ||
| long elapsedTime = System.currentTimeMillis() - startTime; | ||
| HttpStatus status = responseExtractor.extractExceptionStatus(e); | ||
|
|
||
| log.warn("예외 발생: {} \n- 예외 코드: {} \n- 예외 타입: {} \n- 예외 메세지: {} \n- 실행 시간: {}ms", | ||
| methodName, | ||
| status, | ||
| e.getClass().getSimpleName(), | ||
| e.getMessage(), | ||
| elapsedTime); | ||
|
|
||
| throw e; | ||
| } finally { | ||
| MDC.clear(); | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,68 @@ | ||
| package com.somemore.global.aspect.log.extractor; | ||
|
|
||
| import com.fasterxml.jackson.core.JsonProcessingException; | ||
| import com.fasterxml.jackson.databind.ObjectMapper; | ||
| import com.somemore.global.aspect.log.utils.SensitiveDataMasker; | ||
| import lombok.RequiredArgsConstructor; | ||
| import lombok.extern.slf4j.Slf4j; | ||
| import org.aspectj.lang.ProceedingJoinPoint; | ||
| import org.aspectj.lang.reflect.MethodSignature; | ||
| import org.springframework.stereotype.Component; | ||
| import org.springframework.web.bind.annotation.PathVariable; | ||
| import org.springframework.web.bind.annotation.RequestParam; | ||
|
|
||
| import java.lang.reflect.Parameter; | ||
| import java.util.LinkedHashMap; | ||
| import java.util.Map; | ||
|
|
||
| @RequiredArgsConstructor | ||
| @Slf4j | ||
| @Component | ||
| public class ParameterExtractor { | ||
|
|
||
| private final ObjectMapper objectMapper; | ||
| private final SensitiveDataMasker sensitiveDataMasker; | ||
|
|
||
| public String extractParameters(ProceedingJoinPoint joinPoint) { | ||
| try { | ||
| MethodSignature signature = (MethodSignature) joinPoint.getSignature(); | ||
| Parameter[] parameters = signature.getMethod().getParameters(); | ||
| Object[] args = joinPoint.getArgs(); | ||
|
|
||
| if (parameters.length == 0) { | ||
| return "{}"; | ||
| } | ||
|
|
||
| Map<String, Object> paramMap = new LinkedHashMap<>(); | ||
| for (int i = 0; i < parameters.length; i++) { | ||
| if (args[i] != null) { | ||
| addParameter(paramMap, parameters[i], args[i]); | ||
| } | ||
| } | ||
|
|
||
| return objectMapper.writeValueAsString(paramMap); | ||
| } catch (Exception e) { | ||
| log.warn("파라미터 변환 실패: {}", e.getMessage()); | ||
| return "{}"; | ||
| } | ||
| } | ||
|
|
||
| private void addParameter(Map<String, Object> paramMap, Parameter parameter, Object value) throws JsonProcessingException { | ||
| String paramName = extractParamName(parameter); | ||
| paramMap.put(paramName, sensitiveDataMasker.maskSensitiveData(paramName, value, objectMapper)); | ||
| } | ||
|
|
||
| private String extractParamName(Parameter parameter) { | ||
| PathVariable pathVariable = parameter.getAnnotation(PathVariable.class); | ||
| if (pathVariable != null && !pathVariable.value().isEmpty()) { | ||
| return pathVariable.value(); | ||
| } | ||
|
|
||
| RequestParam requestParam = parameter.getAnnotation(RequestParam.class); | ||
| if (requestParam != null && !requestParam.value().isEmpty()) { | ||
| return requestParam.value(); | ||
| } | ||
|
|
||
| return parameter.getName(); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| package com.somemore.global.aspect.log.extractor; | ||
|
|
||
| import jakarta.servlet.http.HttpServletRequest; | ||
| import org.springframework.stereotype.Component; | ||
| import org.springframework.web.context.request.RequestContextHolder; | ||
| import org.springframework.web.context.request.ServletRequestAttributes; | ||
|
|
||
| @Component | ||
| public class RequestExtractor { | ||
|
|
||
| public HttpServletRequest getCurrentRequest() { | ||
| ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); | ||
| if (attributes == null) { | ||
| throw new IllegalStateException("요청을 찾을수 없습니다."); | ||
| } | ||
| return attributes.getRequest(); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,52 @@ | ||
| package com.somemore.global.aspect.log.extractor; | ||
|
|
||
| import com.fasterxml.jackson.databind.ObjectMapper; | ||
| import com.somemore.global.common.response.LoggedResponse; | ||
| import com.somemore.global.exception.DuplicateException; | ||
| import com.somemore.global.exception.ImageUploadException; | ||
| import com.somemore.global.exception.BadRequestException; | ||
| import com.somemore.global.exception.NoSuchElementException; | ||
| import lombok.RequiredArgsConstructor; | ||
| import lombok.extern.slf4j.Slf4j; | ||
| import org.springframework.http.HttpStatus; | ||
| import org.springframework.http.ResponseEntity; | ||
| import org.springframework.stereotype.Component; | ||
| import org.springframework.web.bind.MethodArgumentNotValidException; | ||
|
|
||
| @RequiredArgsConstructor | ||
| @Slf4j | ||
| @Component | ||
| public class ResponseExtractor { | ||
|
|
||
| private final ObjectMapper objectMapper; | ||
|
|
||
| public LoggedResponse extractResponse(Object result) { | ||
| try { | ||
| if (result == null) { | ||
| return new LoggedResponse(HttpStatus.OK, "null"); | ||
| } | ||
|
|
||
| if (result instanceof ResponseEntity<?> responseEntity) { | ||
| String body = objectMapper.writeValueAsString(responseEntity.getBody()); | ||
| return new LoggedResponse(responseEntity.getStatusCode(), body); | ||
| } | ||
|
|
||
| return new LoggedResponse(HttpStatus.OK, objectMapper.writeValueAsString(result)); | ||
| } catch (Exception e) { | ||
| log.warn("응답 변환 실패: {}", e.getMessage()); | ||
| return new LoggedResponse(HttpStatus.OK, "[응답 변환 실패]"); | ||
| } | ||
| } | ||
|
|
||
| public HttpStatus extractExceptionStatus(Exception e) { | ||
| if (e instanceof BadRequestException || | ||
| e instanceof ImageUploadException || | ||
| e instanceof DuplicateException || | ||
| e instanceof MethodArgumentNotValidException) { | ||
| return HttpStatus.BAD_REQUEST; | ||
| } else if (e instanceof NoSuchElementException) { | ||
| return HttpStatus.NOT_FOUND; | ||
| } | ||
| return HttpStatus.INTERNAL_SERVER_ERROR; | ||
| } | ||
|
Comment on lines
+41
to
+51
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 상위 커스텀 예외 클래스를 활용하는 방안도 있을 것 같습니다. 또, BadRequestException 예외가 너무 넓은 부분을 처리하지 않는지도 생각해볼 수 있을 것 같습니다.
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
BadReq와 여러 Exception을 포함해서 BadReq status 를 반환하는 것에서 찝찝함을 느껴서 코멘트했습니다.
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 이제 확인했네요 죄송합니다 |
||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
오타 발견했습니다 ㅎㅎ
throw new IllegalStateException("요청을 찾을수 없습니다.");throw new IllegalStateException("요청을 찾을 수 없습니다.");