Controller에서 RequestMapping된 모든 요청에 로그를 남기고 싶은데, 메소드 하나하나에 로그를 찍는 것은 너무 생산성이 떨어지기에 Spring AOP를 활용하여 로그를 남겼던 방법을 기록합니다.
1. Dependency 추가
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
2. @Aspect 클래스 작성
import java.util.HashMap;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.CodeSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
@Aspect
@Component
public class LoggingAspect {
// om.ocko.myproject.controller 이하 패키지의 모든 클래스 이하 모든 메서드에 적용
@Pointcut("execution(* com.ocko.myproject.controller..*.*(..))")
private void onRequest() {
}
// Pointcut에 의해 필터링된 경로로 들어오는 경우 메서드 호출 전에 적용
@Before("onRequest()")
public void beforeParameterLog(JoinPoint joinPoint) {
Class clazz = joinPoint.getTarget().getClass();
Logger logger = LoggerFactory.getLogger(clazz);
HttpServletRequest request =
((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
logger.info("Request : {} {} {}", request.getMethod(), request.getRequestURI(), params(joinPoint).toString());
}
// Poincut에 의해 필터링된 경로로 들어오는 경우 메서드 리턴 후에 적용
@AfterReturning(value = "onRequest()", returning = "returnObj")
public void afterReturnLog(JoinPoint joinPoint, Object returnObj) {
Class clazz = joinPoint.getTarget().getClass();
Logger logger = LoggerFactory.getLogger(clazz);
HttpServletRequest request =
((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
logger.info("Response {} : {}", request.getRequestURI(), returnObj);
}
/* printing request parameter or request body */
private Map params(JoinPoint joinPoint) {
CodeSignature codeSignature = (CodeSignature) joinPoint.getSignature();
String[] parameterNames = codeSignature.getParameterNames();
Object[] args = joinPoint.getArgs();
Map<String, Object> params = new HashMap<>();
for (int i = 0; i < parameterNames.length; i++) {
params.put(parameterNames[i], args[i]);
}
return params;
}
}
AOP 주요 개념
- Aspect : 흩어진 관심사를 모듈화 한 것
- Advice : 실질적인 부가기능을 담은 구현체 (@Before, @After, @AfterReturning, @AfterThrowing, @Around)
- JointPoint : Advice가 적용될 위치, 끼어들 수 있는 지점.
- PointCut : JointPoint의 상세한 스펙을 정의한 것.
3. 실행결과
2023-03-29 18:58:02.491 INFO 162004 --- [nio-8081-exec-3] i.s.k.p.controller.AdminApiController : Request : GET /api/a/board {id=165}
2023-03-29 18:58:02.521 INFO 162004 --- [nio-8081-exec-3] i.s.k.p.controller.AdminApiController : Response /api/a/board : {id=165, title=hello, content=hello world}
Rererence
'Programming > Spring Boot' 카테고리의 다른 글
[Spring Security] 수동 인증(강제 로그인) (0) | 2023.05.16 |
---|---|
[Jasypt] Spring Boot 애플리케이션의 프로퍼티 암호화 (0) | 2023.03.24 |
[Maven] 라이브러리 Dependency 충돌 해결하기(Maven Tree) (0) | 2023.03.02 |
[Mybatis] PostgreSQL ilike 구현하기(feat. Criteria 커스텀하기) (0) | 2022.08.20 |
[Spring Security] 두개의 로그인 페이지(Multiple Login Pages) (0) | 2022.08.10 |