Friday, August 16, 2019

Spring Aspects - Logging debugging information of service methods


Method Tracing Aspects

Following aspects enable method tracing by using Spring AOP. The following information will be gathered and logged:


  1. Types and values of input parameters
  2. Type and value of return object
  3. Types and details of exceptions





@Configuration
@Aspect
public class MethodTracingAspect {
  private static Logger logger = LoggerFactory.getLogger(MethodTracingAspect.class);

  @Autowired private LoggerCache loggerCache;

  @PostConstruct
  protected void init() {
    logger.warn("###-AOP-### DebuggableAspect initialized...");
  }

  @Before(
      "(com.yourcorp.common.aspect.CommonPointcut.servicePackageMethodDebugEnabled() "
          + "|| com.yourcorp.common.aspect.CommonPointcut.implPackageMethodDebugEnabled()) && "
          + "!com.yourcorp.common.aspect.CommonPointcut.nonTraceableAnnotationEnabled()")
  public void logDebugInformationMethodBeforeEntering(JoinPoint joinPoint) {
    final Logger mLogger = loggerCache.getLogger(joinPoint);
    mLogger.debug(
        "Entering class:{} - method : {} with input parameters {}",
        joinPoint.getTarget().getClass().getSimpleName(),
        joinPoint.getSignature().getName(),
        joinPoint.getArgs());
  }

  @AfterReturning(
    value =
        "(com.yourcorp.common.aspect.CommonPointcut.servicePackageMethodDebugEnabled() "
            + "|| com.yourcorp.common.aspect.CommonPointcut.implPackageMethodDebugEnabled()) && "
            + "!com.yourcorp.common.aspect.CommonPointcut.nonTraceableAnnotationEnabled()",
    returning = "result"
  )
  public void logDebugInformationMethodAfterReturning(JoinPoint joinPoint, Object result) {
    final Logger mLogger = loggerCache.getLogger(joinPoint);
    mLogger.debug(
        "Returning from class:{} - method : {} with return type: {} - value {}",
        joinPoint.getTarget().getClass().getSimpleName(),
        joinPoint.getSignature().getName(),
        ((MethodSignature) (joinPoint.getSignature())).getReturnType().getSimpleName(),
        result);
  }
}

@Configuration
@Aspect
public class ExceptionHandlingAspect {
  @Autowired private LoggerCache loggerCache;

  @AfterThrowing(
    value = "com.yourcorp.common.aspect.CommonPointcut.servicePackageMethodDebugEnabled()",
    throwing = "exception"
  )
  public void logRecoverableFailureException(
      JoinPoint joinPoint, RecoverableFailureException exception) {
    Logger logger = loggerCache.getLogger(joinPoint);
    logger.warn("\t " + exception.getFullMessage(), exception);
  }

  @AfterThrowing(
    value = "com.yourcorp.common.aspect.CommonPointcut.servicePackageMethodDebugEnabled()",
    throwing = "exception"
  )
  public void logUnRecoverableFailureException(
      JoinPoint joinPoint, UnRecoverableFailureException exception) {
    Logger logger = loggerCache.getLogger(joinPoint);
    logger.error("\t " + exception.getFullMessage(), exception);
  }
}



Common Pointcuts




import org.aspectj.lang.annotation.Pointcut;

/**
 * The pointcuts defined here are the service classes most likely logging, metrics and tracing
 * concerns will be the applied.
 */
@Aspect
public class CommonPointcut {

  // All method in service packages
  @Pointcut("execution(* com.yourcorp.app..service.*.*(..))")
  public void servicePackageMethodDebugEnabled() {
    // no implementation.
  }

  // All method in impl packages
  @Pointcut("execution(* com.yourcorp.app..impl..*.*(..))")
  public void implPackageMethodDebugEnabled() {
    // no implementation.
  }

  // Methods with @MethodDebuggingDisable
  @Pointcut("@annotation(com.yourcorp.app.common.aspect.debug.MethodDebuggingDisable))")
  public void nonTraceableAnnotationEnabled() {
    // no implementation.
  }
}



Custom Annotation



/**
 * If a method is very sensitive to
 */
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MethodTracingDisable {}


Logger Cache



/**
 * Cache logger according to type, the logger search operation is expensive.
 */
@Service
public class LoggerCacheImpl implements LoggerCache {
  private Map<Class<?>, Logger> loggerCache = new ConcurrentReferenceHashMap<>();

  @Override
  @MethodTracingDisable
  public Logger getLogger(JoinPoint joinPoint) {
    final Class<?> clazz = joinPoint.getTarget().getClass();
    return loggerCache.computeIfAbsent(clazz, e -> LoggerFactory.getLogger(e));
  }
}


No comments:

Post a Comment