aop를 활용하여 메소드 실행중 슬로우 쿼리와 오류 발생시 해당 상황을 로그테이블에 저장하는 기능을 만들어 보았다.
@Aspect
@Component
public class LogTraceAspect {
private static final long SLOW_QUERY_THRESHOLD = 1000; // 슬로우 쿼리 기준
private final ThreadLocal<Long> startTime = new ThreadLocal<>();
// 클래스 이름이 서비스인것을 추적
@Pointcut("execution(* *..*Service.*(..))")
private void allService() {}
@Before("allService()")
public void beforeMethod() {
startTime.set(System.currentTimeMillis());
}
@After("allService()")
public void afterMethod(JoinPoint joinPoint) {
long endTime = System.currentTimeMillis();
long elapsedTime = endTime - startTime.get();
if (elapsedTime > SLOW_QUERY_THRESHOLD) {
logSave();
}
}
@AfterThrowing(value = "allService()", throwing = "exception")
public void afterThrowing(JoinPoint joinPoint, Exception exception) {
logSave();
}
}
Service 클래스에 있는 특정 기능에 sleep을 걸고 메소드를 실행하였다. log가 잘 저장되었을까
public void delayed() {
try {
Thread.sleep(2000);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
엣 저장 실패
Connection is read-only. Queries leading to data modification are not allowed
성능 향상의 이유로 Service 클래스 상단에 @Transactional(readOnly = true) 어노테이션을 해놨는데 로그 저장 메소드를 이용하기 위해선 별도의 트랜잭션이 필요함
해결 방법 : 로그 저장시 새로운 트랜잭션을 생성하고 현재 트랜잭션이 있으면 일시 중단하고 로그 저장이 완료되면 원래 트랜잭션 다시 시작
@Override
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void logSave() {
// 로그 저장..
}
@Transactional(propagation = Propagation.REQUIRES_NEW) 사용할 때 주의할 점
내가 만든 로그 저장 같은 기능은 성능에 큰 영향을 주지 않을거 같음