2017-11-22 35 views
1

TraceAspect 구현으로 사용자 정의 @Traceable 주석이 있습니다. 메소드 호출 전후에 기록 할 메소드에 @Traceable 주석을 사용합니다. 방금 value 속성을 통해 사용하는 로그 수준을 @Traceable (이전 버전은 항상 INFO 만 사용)으로 지정할 수있는 기능이 추가되었습니다. 내가 가지고있는 것은 효과가 있지만 더 효과적 일 수있는 방법이 있는지 궁금해하고 있습니다. 특히 어떤 종류의 aspect 컨텍스트가 있다면 을 @Traceable에 주어진 방법으로 설정하는 것을 앱 시작시 한 번 수행 할 수 있습니다.내 aspect를보다 실행 가능하게 만들기 위해 joinpoint 주변의 AOP 전에 일회성 처리를 수행하는 방법이 있습니까?

@Traceable 주석 :

@Documented 
@Retention(RUNTIME) 
@Target(METHOD) 
public @interface Traceable { 

    Level value() default Level.INFO; 
} 

현재 TraceAspect IMPL : 내가 가진

import org.aspectj.lang.ProceedingJoinPoint; 
import org.aspectj.lang.annotation.Around; 
import org.aspectj.lang.annotation.Aspect; 
import org.aspectj.lang.reflect.MethodSignature; 
import org.slf4j.Logger; 
import org.slf4j.LoggerFactory; 
import org.slf4j.event.Level; 
import org.springframework.stereotype.Component; 

@Component 
@Aspect 
public class TraceAspect { 

    @Around("@annotation(com.collaterate.logging.Traceable)") 
    public Object traceAround(ProceedingJoinPoint joinPoint) throws Throwable { 
     Traceable traceable = ((MethodSignature) joinPoint.getSignature()).getMethod().getAnnotation(Traceable.class); 
     Logger classLog = LoggerFactory.getLogger(joinPoint.getSignature().getDeclaringType()); 
     LoggingHelper loggingHelper = getLoggingHelper(traceable, classLog); 

     String methodName = joinPoint.getSignature().getName(); 
     loggingHelper.log("{}() - started", methodName); 
     Object returnVal = joinPoint.proceed(); 
     loggingHelper.log("{}() - ended", methodName); 

     return returnVal; 
    } 

    private LoggingHelper getLoggingHelper(Traceable traceable, Logger classLog) { 
     if (Level.INFO == traceable.value() || null == traceable.value()) { 
      // try to avoid the switch below... most of the time it will be INFO 
      return (format, args) -> classLog.info(format, args); 
     } else { 
      switch (traceable.value()) { 
       case ERROR : 
        return (format, args) -> classLog.error(format, args); 
       case WARN : 
        return (format, args) -> classLog.warn(format, args); 
       case DEBUG : 
        return (format, args) -> classLog.debug(format, args); 
       case TRACE : 
        return (format, args) -> classLog.trace(format, args); 
       default : 
        return (format, args) -> classLog.info(format, args); 
      } 
     } 
    } 

    @FunctionalInterface 
    interface LoggingHelper { 

     void log(String format, Object... args); 

    } 
} 

유일한 다른 아이디어 (각 로그 레벨 일) 후 TraceAspect이를 IMPL 할 여러 주석을 만드는 것입니다 @Around 각 주석에 대한 핸들러이며 런타임에 반사/전환을 피할 수 있습니다. 여기서 싫어하는 점은 이미 여러 프로젝트에서 생산 코드 전체에 기존 @Traceable 주석을 사용하고 있다는 것입니다. 1 주석을 유지하고 속성을 통해 로그 수준을 지정할 수 있습니다.

이론적으로 프록시가 만들어 질 때 응용 프로그램 시작시 모든 정보가 있기 때문에 이론적으로 할 수 있어야합니다. 각 주석이 달린 메소드에는 일종의 컨텍스트가 있어야합니다.

답변

2

메트릭에 대해 비슷한 작업을 수행했습니다. LoggingHelper값이class and method pair의 로그 레지스트리를 유지 관리 할 수 ​​있습니다.

클래스 메서드 쌍에 로그 레지스트리에 항목이 없으면 로깅 도우미를 만들어 레지스트리에 저장하십시오. 두 번째로는 레지스트리에서 찾아보기 만하면됩니다.

레지스트리는 사용자 환경에 자동으로 지정되어야하는 사용자 정의 스프링 빈입니다.

다음은 수정 된 TraceAspect의 예입니다.

@Component 
@Aspect 
public class TraceAspect { 

    private LogRegistry registry; 

    @Autowired 
    public TraceAspect(LogRegistry registry) { 
     this.registry = registry; 
    } 

    @Around("@annotation(com.collaterate.logging.Traceable)") 
    public Object traceAround(ProceedingJoinPoint joinPoint) throws Throwable { 
     String loggerName = joinPoint.getSignature() 
      .getDeclaringType().getCanonicalName() + "." 
       + joinPoint.getSignature().getName(); 

     LoggingHelper loggingHelper = registry.get(loggerName); 
     if (loggingHelper == null) { 
      Traceable traceable = ((MethodSignature) joinPoint.getSignature()).getMethod().getAnnotation(Traceable.class); 
      Logger classLog = LoggerFactory.getLogger(joinPoint.getSignature().getDeclaringType()); 
      loggingHelper = getLoggingHelper(traceable, classLog); 
      registry.put(loggerName, loggingHelper) 
     } 

     String methodName = joinPoint.getSignature().getName(); 
     loggingHelper.log("{}() - started", methodName); 
     Object returnVal = joinPoint.proceed(); 
     loggingHelper.log("{}() - ended", methodName); 

     return returnVal; 
    } 
} 
+0

'LogRegistry'를 사용합니까? 'Map' 대신에 자신의 bean으로 사용하는 것이 요점은 무엇입니까? – JeffNelson

+0

필자의 경우, 다른 Spring 구성 요소가 레지스트리에 액세스했다. 엄지 규칙으로, 나는 일반적으로 전역 캐시/레지스트리를 별도의 Spring 관리 빈에 보관하여 여러 Spring 관리 빈이이를 액세스 할 수있게한다. Spring 구성 요소는 기본적으로 싱글 톤이므로 맵을 사용할 수도 있습니다. –