https://lion-king.tistory.com/entry/Spring-http-request-logging-with-aop-1
이전 글에서 AOP를 활용하여 http request를 로깅하기로 결정하였습니다.
AOP에 대한 간단한 설명 ↓
https://lion-king.tistory.com/44
먼저, 구현한 코드를 보겠습니다.
Aspect 정의
@Aspect
@Order(Ordered.HIGHEST_PRECEDENCE)
@Component
@Slf4j
public class LoggingAspect {
// RestController,Controller public Method
@Around("(within(@org.springframework.stereotype.Controller *)"
+ "|| within(@org.springframework.web.bind.annotation.RestController *))"
+ "&& execution(public * *(..))")
public Object controllerMethodLogging(ProceedingJoinPoint joinPoint) throws Throwable {
try {
Object result = joinPoint.proceed();
...
// 로깅 실행
return result;
} catch(Throwable rethrow) {
// 메소드에서 정의하고 처리 가능한 예외에 대한 메세지 로깅
throw rethrow; // throw 후 ControllerAdvice 에서 처리. 정의되지 않은 Exception 은 error log 발생
}
}
@Order : 가장 높은 레벨을 주었습니다. 가장 먼저 이 로깅하는 기능이 수행되도록 했습니다. (다른 Aspect가 존재하는 경우)
@Around : 구현 목적이 Controller 메소드 호출 시 http request를 로깅하는 것이기 때문에 Controller, RestController 클래스의 public method 로 설정하였습니다. @Before를 사용하여 메소드 시작 전에 수행하도록 할 수도 있었지만, 메소드 수행시간을 계산하는 기능을 구현하는데 어려움이 있고 예외 상황에 따라 다른 로깅을 필요로 하여 @Around로 변경하였습니다.
ProceedingJoinPoint : 해당 객체로 타깃 메소드의 시작점을 정할 수 있습니다. 위 코드에서는 로깅 내용을 만드는등의 작업을 한 후 타깃 메소드를 실행합니다.
@Order(Ordered.HIGHEST_PRECEDENCE)
@ControllerAdvice
@AllArgsConstructor
@Slf4j
public class CustomControllerExceptionHandler {
@ExceptionHandler(value = ExampleException.class)
public ResponseEntity<?> exampleException(HttpServletRequest req, Exception e) {
log.info("Example Exception : {} ", e.getMessage());
...
}
}
예외 상황 발생 시 수행할 작업을 특정 Exception 마다 명시하여 처리합니다.
자세한 내용은 @ControllerAdvice 를 참고
@Order() // default Ordered.LOWEST_PRECEDENCE
@ControllerAdvice
@Slf4j
public class GlobalControllerExceptionHandler {
@ExceptionHandler(value = Exception.class)
public ResponseEntity<ApiResponse> unexpectedException(HttpServletRequest request,
Exception e) {
log.error("Unexpected Exception : {} ", e.getMessage());
...
}
...
최상위 Exception Class를 처리하는 작업을 명시하였습니다.
개발자가 정의한 Exception 과 그렇지 않은 Exception 으로 구분하여 처리하도록
Custom / Global Handler로 나누어 명시하였습니다.
Custom은 따로 수행할 동작을 정의해야하고, Global은 무조건 Error Log를 발생시킨 후 오류 응답을 반환합니다.
요청 & 응답 흐름도
- 오류없이 정상적으로 요청을 응답한 경우
- 예외가 발생한 경우
최종적으로 위의 흐름도 대로 응답하게 됩니다.
결과
정리된 코드가 보이시나요? 이부분은 극히 일부분이고 실제로 매우 많은 수의 메소드를 정리 할 수 있습니다.
이 작업을 완료하고나서 얻은 이점은 다음과 같다고 생각합니다.
1. 메소드가 핵심기능에 집중되도록 개선되어 개발자가 한눈에 파악하기 쉬워졌습니다.
2. Logging 하는 기능을 한곳에서 담당하여 Logging 포맷이나 레벨 등을 한에서 관리 할 수 있게되었습니다.
3 .개발자가 실수로 Logging 하는 부분을 빼먹을 일이 사라졌습니다.
읽어주셔서 감사합니다!
By. Ryankim
'Spring&Spring Boot' 카테고리의 다른 글
(Spring / Design pattern ) 템플릿 메소드 패턴, 전략 패턴 (0) | 2020.05.12 |
---|---|
(Spring/Redis/@Cacheable) Spring Cacheable로 캐싱기능 적용 (0) | 2020.03.21 |
(Spring/Spring Boot) transactional annotation 속성 격리 isolation (0) | 2020.02.19 |
(Spring/Spring Boot) transactional annotation 속성 전파 propagation (2) | 2020.02.01 |
(Spring AOP) AOP란? AOP 핵심 간단 정리 (0) | 2020.01.09 |
댓글