Spring&Spring Boot

(Spring Controller Logging) Spring에서 http request 로깅하기 - 2

Developer RyanKim 2020. 2. 21. 16:43


https://lion-king.tistory.com/entry/Spring-http-request-logging-with-aop-1

 

(Spring Controller Logging) Spring에서 http request 로깅하기 - 1

Spring Controller Logging - Spring에서 http request 로깅하기 @GetMapping("/{code}") public ResponseEntity example( @PathVariable(name = "code") String code { final String requestUri = request.ge..

lion-king.tistory.com

이전 글에서 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