상세 컨텐츠

본문 제목

어노테이션으로 로깅(Logging) 처리하기

IT/Spring 및 Boot

by SINAFLA 2021. 6. 29. 09:00

본문

반응형

  • 개발자들은 일을 간편하게 하기 위해서 에러 로그와 같은 로그를 DB에 기록에 남긴다.
  • 에러가 난 부분과 난 이유를 분석한다.
  • 그래서는 메소드가 호출되었을 때 받은 파라미터 값과 그 결과 값이 필요할 때 있다. 결과 값은 호출된 메소드에 명시되었기 때문에 중요한 건 넘겨 받은 파라미터 값이 더 중요하다.
  • 제대로 된 값이 넘어온 지 값 자체를 확인할 필요가 있다.

 

개발 환경

  • SpringBoot 2.3.6 으로 개발

 

어노테이션 구현

package com.dev.log;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.TYPE, ElementType.METHOD}) 
@Retention(RetentionPolicy.RUNTIME)
public @interface Logging {

}
  • 어노테이션 구현 시 interface로 구현을 한다.

 

소스 설명

  • 해당 어노테이션을 선언한 곳은 클래스/인터페이스/열거/메소드에 부착할 수 있다.

@Target

  • 어노테이션이 부착될 수 있는 타입을 지정한다.
  • 타입이란 클래스 / 생성자 / 메서드 등을 말한다.

 

종류

  • ElementType.TYPE : 클래스 / 인터페이스 / 열거 타입에 어노테이션을 부착할 수 있다.
  • ElementType.CONSTRUCTOR : 생성자에 어노테이션을 부착할 수 있다.
  • ElementType.METHOD : 메소드에 어노테이션을 부착할 수 있다.
  • ElementType.FIELD : 필드(변수)에 어노테이션을 부착할 수 있다.

 

매개변수

  • ANOOTATION_TYPE : 어노테이션
  • LOCAL_VARIABLE : 지역(로컬) 변수
  • PACKAGE : 패키지

@Retention

  • 어느 시점까지 어노테이션의 메모리를 가져갈 지 설정한다.

 

RetentionPolicy (Retention 어노테이션 메모리 정책)

  • SOURCE : 어노테이션을 사실상 주석처럼 사용하는 것. 컴파일할 때 해당 어노테이션의 메모리를 버린다.
  • CLASS : 컴파일러가 컴파일에서는 어노테이션을 메모리를 가져가지만 런타임시에는 사라진다.
  • RUNTIME : 어노테이션을 런타임시에까지 사용할 수 있다.

어노테이션을 이용한 로깅 AOP 구현

package com.dev.log;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@RequiredArgsConstructor
@Aspect
@Component
public class LoggingAop {
	Logger log = LoggerFactory.getLogger(LoggingAop.class); 
	
	@Before(value = "@annotation(logging)", argNames="joinPoint, logging")
	public void log(JoinPoint joinPoint, Logging logging) throws Throwable{
		geValue(joinPoint, logging);
	}
	
	void geValue(JoinPoint joinPoint, Logging logging) {
        log.info(">>>>> " + getClassName(joinPoint, logging) + " / " + getMethodName(joinPoint) + " / " + getParameter(joinPoint));
    }
	
	// 메소드명 가져오기
	public String getMethodName(JoinPoint joinPoint) {
		MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        return method.getName();
	}
	
	// 클래스명 가져오기
	public String getClassName(JoinPoint joinPoint, Logging logging) {
		Class<?> activeClass = joinPoint.getTarget().getClass();
		logging= activeClass.getAnnotation(Logging.class);
		return activeClass.getName();
	}
	
	// 매개변수 가져오기
	public List<String> getParameter(JoinPoint joinPoint) {
		List<String> parameterNames = new ArrayList<>();
		
		Stream.of(joinPoint.getArgs()).forEach(it -> {
        	parameterNames.add(it.toString());
        });
		return parameterNames;
	}
}
  • AOP를 구현하기 위해선 AOP 구현 클래스를 Aspect 어노테이션도 등록하고, 다른 곳에서도 사용하기 위해선 빈으로 등록하고 사용해야 한다.
  • AOP 구현 클래스 위에 @Aspect 어노테이션과 @Component 어노테이션을 선언해야 한다.

 

소스 설명

  • getMethodName() : 메소드명 가져오는 메소드
  • getClassName() : 어노테이션이 붙은 클래스명을 가져오는 메소드
  • getParameter() : 메소드의 파라미터를 가져오는 메소드

@Aspect

  • 애스펙트는 부가기능을 정의한 코드인 어드바이스(Advice)와 어드바이스를 어디에 적용할지 결정하는 포인트컷(Pointcut)을 합친 개념이다.
  • Advice + PointCut = Aspect
  • @Aspect 어노테이션을 사용했다고 해서 자동으로 빈으로 등록되는 게 아니기 때문에 @Component 어노테이션을 이용해서 빈으로 등록해야 한다.

 

@Before

  • 메소드 실행 전에 실행된다고 선언한다.
  • value : AOP가 실행할 어떤게 실행할 때 AOP가 적용할 것인지 명시해준다.
  • argNames : AOP 메소드 실행 시 매개변수를 말함

 

 

반응형

관련글 더보기

댓글 영역