์ƒ์„ธ ์ปจํ…์ธ 

๋ณธ๋ฌธ ์ œ๋ชฉ

[์€ผ] Spring AOP ์‚ฌ์šฉํ•˜์—ฌ Loggingํ•˜๊ธฐ

๋ณธ๋ฌธ

728x90

 

์ด์ „์—๋Š” ๊ฐ๊ฐ์˜ controller ์ตœ์ƒ๋‹จ์—์„œ ์–ด๋–ค ๊ฐ’์„ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ๋ฐ›์•˜๋Š”์ง€ ์ง์ ‘ ๋กœ๊น…์„ ์ง„ํ–‰ํ–ˆ๋‹ค

๊ทธ๋ฆฌ๊ณ  ์‹คํ–‰ ์‹œ๊ฐ„์— ๋Œ€ํ•œ ๋กœ๊น…์„ ํ•˜์ง€ ์•Š์•˜๋‹ค.

 

 

๋น„์ฆˆ๋‹ˆ์Šค ์ฝ”๋“œ์— ์˜ํ–ฅ์„ ์ฃผ์ง€ ์•Š์œผ๋ฉด์„œ๋„ (log.debug ์—„์ฒญ ๋งŽ์œผ๋‹ˆ๊นŒ ๋ณต์žกํ•˜๋”๋ผ)

Controller, Service, Repository ๋ฉ”์„œ๋“œ ํ˜ธ์ถœํ•  ๋•Œ๋งˆ๋‹ค ์‹œ์ž‘์‹œ๊ฐ„,  ๋์‹œ๊ฐ„์„ ๋‚จ๊ธฐ๊ณ  ์‹ถ์—ˆ๋‹ค. 

 

 

๋”ฐ๋ผ์„œ, Spring AOP ์‚ฌ์šฉํ•˜์—ฌ Logging ์ž‘์—…์„ ์ง„ํ–‰ํ•˜๊ธฐ๋กœ ํ–ˆ๋‹ค.


๋Œ€๋žต ์ด๋ ‡๊ฒŒ ์ž‘์„ฑํ–ˆ๋‹ค

@Aspect
@Component
public class LoggingAspect {
	private static final Logger logger = LoggerFactory.getLogger(LoggingAspect.class);

	// ThreadLocal๋กœ ์‹œ์ž‘ ์‹œ๊ฐ„์„ ์ €์žฅ
	private static ThreadLocal<Long> startTimeThreadLocal = new ThreadLocal<>();

	@Pointcut("within(com.exciting.vvue..*) && within(@org.springframework.web.bind.annotation.RestController *)")
	public void controllerMethods() {}

	@Pointcut("within(com.exciting.vvue..*) && within(@org.springframework.stereotype.Service *)")
	public void serviceMethods() {}

	@Pointcut("within(com.exciting.vvue..*) && within(@org.springframework.stereotype.Repository *)")
	public void repositoryMethods() {}

	@Around("controllerMethods() || serviceMethods() || repositoryMethods()")
	public Object logAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
		// ์‹œ์ž‘ ์‹œ๊ฐ„์„ ThreadLocal์— ์ €์žฅ
		long startTime = System.nanoTime();
		startTimeThreadLocal.set(startTime);

		String currentTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS"));
		logger.info("[INFO] [{}] [{}] {} | started with arguments: {}",
			resolvePointcutName(proceedingJoinPoint),
			currentTime,
			proceedingJoinPoint.getSignature().toShortString(),
			proceedingJoinPoint.getArgs());

		try {
			// ์‹ค์ œ ๋ฉ”์„œ๋“œ ํ˜ธ์ถœ
			Object result = proceedingJoinPoint.proceed(); // ProceedingJoinPoint์—์„œ proceed() ํ˜ธ์ถœ

			// ๋ฉ”์„œ๋“œ ์‹คํ–‰ ํ›„ ์ข…๋ฃŒ ์‹œ๊ฐ„ ๊ณ„์‚ฐ
			long endTime = System.nanoTime();
			long duration = (endTime - startTime) / 1_000_000; // ๋ฐ€๋ฆฌ์ดˆ๋กœ ๋ณ€ํ™˜
			logger.info("[INFO] [{}] [{}] {} | duration: {} ms. Return value: {}",
				resolvePointcutName(proceedingJoinPoint),
				currentTime,
				proceedingJoinPoint.getSignature().toShortString(),
				duration,
				result);

			return result;
		} catch (Throwable throwable) {
			// ์˜ˆ์™ธ ๋ฐœ์ƒ ์‹œ ์ฒ˜๋ฆฌ
			long endTime = System.nanoTime();
			long duration = (endTime - startTime) / 1_000_000; // ๋ฐ€๋ฆฌ์ดˆ๋กœ ๋ณ€ํ™˜
			logger.error("[ERROR] [{}] [{}] {} | threw exception after {} ms: {}",
				resolvePointcutName(proceedingJoinPoint),
				currentTime,
				proceedingJoinPoint.getSignature().toShortString(),
				duration,
				throwable.getMessage());
			throw throwable;
		} finally {
			// ThreadLocal ๊ฐ’ ์ •๋ฆฌ
			startTimeThreadLocal.remove();
		}
	}

	private String resolvePointcutName(ProceedingJoinPoint proceedingJoinPoint) {
		Class<?> clazz = proceedingJoinPoint.getSignature().getDeclaringType();
		Class<?> superClass = clazz.getSuperclass();

		if (clazz.isAnnotationPresent(org.springframework.web.bind.annotation.RestController.class)
			|| superClass != null && superClass.isAnnotationPresent(org.springframework.web.bind.annotation.RestController.class)) {
			return "controller";
		}

		if (clazz.isAnnotationPresent(org.springframework.stereotype.Service.class)
			|| superClass != null && superClass.isAnnotationPresent(org.springframework.stereotype.Service.class)) {
			return "service";
		}

		if (clazz.isAnnotationPresent(org.springframework.stereotype.Repository.class)
			|| superClass != null && superClass.isAnnotationPresent(org.springframework.stereotype.Repository.class)) {
			return "repository";
		}

		return "unknown";
	}
}



๋กœ๊น… ๊ฒฐ๊ณผ๋Š” ์•„๋ž˜์™€ ๊ฐ™๋‹ค

 

 


๋”๋ณด๊ธฐ

๋งŒ์•ฝ์— ํŠน์ • ํ•จ์ˆ˜์—๋งŒ ๋กœ๊น…์„ ์ง„ํ–‰ํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด,  

 

์•„๋ž˜์™€ ๊ฐ™์ด ์–ด๋…ธํ…Œ์ด์…˜์„ ์ž‘์„ฑํ•˜๊ณ  

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface LogExecuteTime {
}

 

@Aspect
@Component
public class ExecuteTimeAspect {

	@Around("@annotation(LogExecuteTime)") // ์–ด๋…ธํ…Œ์ด์…˜ ๊ธฐ์ค€์œผ๋กœ ์ง„ํ–‰
	public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
		long start = System.currentTimeMillis();
        
		Object result = joinPoint.proceed(); // ์‹ค์ œ ์‹คํ–‰ 
		
        long end = System.currentTimeMillis();
   
		System.out.println((end - start) + "ms");
		return result;
	}
}

 

728x90

๊ด€๋ จ๊ธ€ ๋”๋ณด๊ธฐ