AOP를 통해 모든 request parameter(혹은 requset body)와 response를 로그를 찍어보기로 한다.현재 컨트롤러에서 수동으로 각각의 endpoint마다 로그를 찍는 코드가 추가되어 있는데, AOP를 이용하면 좋을 것 같았다!
build.gradle에 의존성을 추가해준다.
aop라는 패키지를 생성하고, 패키지 하위에 LoggingAspect라는 클래스를 만들었다..
controller 패키지 하위의 모든 public 메서드와 매칭시킨다.@annotation을 이용하면 애노테이션별로 매칭시킬 수도 있다. @PostMapping과 매칭시켜서 POST요청에만 로그를 찍는다던지..
/* controller 패키지에 포함된 public 메서드와 매칭 */
@Pointcut("within(test.rest.api.controller..*)")
public void onRequest() { }
참고로 애노테이션으로 매칭시키는 방법이다.
// POST
@Pointcut("@annotation(org.springframework.web.bind.annotation.PostMapping)")
// GET
@Pointcut("@annotation(org.springframework.web.bind.annotation.GetMapping)")
Advice는 실제로 실행될 내용을 적는 부분이다.
Logger를 매칭되어 실행될 메서드의 클래스를 통해 생성한다return value가 반환된다 (ResponseEntity)joinPoint.proceed()를 통해 프록시가 호출되고, 프록시가 실제 메서드를 호출한다finally 블록에서 requstURI, parameters, response 로그를 찍는다 /* Pointcut 과 매칭되는 메서드의 실행 전, 후에 실행
* @Around advice 는 꼭 proceed()가 필요하다. */
@Around("onRequest()")
public Object logAction(ProceedingJoinPoint joinPoint) throws Throwable{
Class clazz = joinPoint.getTarget().getClass();
Logger logger = LoggerFactory.getLogger(clazz);
Object result = null;
try {
result = joinPoint.proceed(joinPoint.getArgs());
return result;
} finally {
logger.info(getRequestUrl(joinPoint, clazz));
logger.info("parameters" + JSON.toJSONString(params(joinPoint)));
logger.info("response: " + JSON.toJSONString(result, true));
}
}
JoinPoint와 joinPoint가 실행된 class를 이용해 요청 URI를 구한다.
HttpServletRequset를 통해 구하는 방법도 있지만, 이 방법을 택했다.