¿Cómo usar AOP con AspectJ para el registro?

Me gustaría agregar mensajes de “rastreo” a todos mis métodos públicos de la siguiente manera:

  public void foo (s: String, n: int) {// log es un registrador log4j o cualquier otra biblioteca
   log.trace (String.format ("Enter foo con s:% s, n:% d", s, n))
   ...
   log.trace ("Salir foo") 
 } 

Ahora me gustaría agregar todos esos log.trace a mis métodos automáticamente con AOP (y la instrumentación de código de bytes). Estoy pensando en AspectJ . ¿Tiene sentido? ¿Conoces alguna fuente abierta, que hace exactamente eso?

He creado un aspecto simple para capturar la ejecución de métodos públicos. El núcleo de este código AspectJ es la definición de punto de corte:

 pointcut publicMethodExecuted(): execution(public * *(..)); 

Aquí estamos capturando todos los métodos públicos con cualquier tipo de devolución, en cualquier paquete y cualquier clase, con cualquier cantidad de parámetros.

La ejecución del consejo se puede visualizar en el siguiente fragmento de código:

 after(): publicMethodExecuted() { System.out.printf("Enters on method: %s. \n", thisJoinPoint.getSignature()); Object[] arguments = thisJoinPoint.getArgs(); for (int i =0; i < arguments.length; i++){ Object argument = arguments[i]; if (argument != null){ System.out.printf("With argument of type %s and value %s. \n", argument.getClass().toString(), argument); } } System.out.printf("Exits method: %s. \n", thisJoinPoint.getSignature()); } 

Este consejo usa thisJoinPoint para obtener la firma y los argumentos del método. Y eso es. Aquí está el código de aspecto:

 public aspect LogAspect { pointcut publicMethodExecuted(): execution(public * *(..)); after(): publicMethodExecuted() { System.out.printf("Enters on method: %s. \n", thisJoinPoint.getSignature()); Object[] arguments = thisJoinPoint.getArgs(); for (int i =0; i < arguments.length; i++){ Object argument = arguments[i]; if (argument != null){ System.out.printf("With argument of type %s and value %s. \n", argument.getClass().toString(), argument); } } System.out.printf("Exits method: %s. \n", thisJoinPoint.getSignature()); } 

Para ejemplos más complejos, recomendaría el libro AspectJ: In Action .

@Loggable anotación @Loggable y un aspecto AspectJ de jcabi-aspects es un mecanismo listo para usted (soy desarrollador):

 @Loggable(Loggable.DEBUG) public String load(URL url) { return url.openConnection().getContent(); } 

Para registrar tanto la entrada como la salida, según los requisitos de la pregunta:

 @Loggable(Loggable.DEBUG, prepend=true) public String load(URL url) { return url.openConnection().getContent(); } 

Todos los registros van a SLF4J. Revisa esta publicación para más detalles.

Puede usar diferentes puntos para hacer su requerimiento. Esta documentación te ayudará.

Solución directa

Puede probar este código abierto http://code.google.com/p/perfspy/ . PerfSpy es una herramienta de registro de tiempo de ejecución, monitoreo de rendimiento e inspección de código. Utiliza ApsectJ para tejer alrededor del código de su aplicación en tiempo de ejecución, y registra el tiempo de ejecución de cada método y sus parámetros y valores de entrada. Tiene una aplicación de interfaz de usuario, en la que puede ver las invocaciones de métodos y sus valores de entrada y retorno como árboles. Con él, puede detectar cuellos de botella de rendimiento y comprender el flujo de código complejo.

Aquí está mi implementación simple para iniciar sesión, salir y registrar Excepciones de métodos

Anotación

 package test; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Documented @Retention(RetentionPolicy.RUNTIME) @Target({ ElementType.METHOD, ElementType.TYPE }) public @interface Audit { } 

Interceptador

 import java.lang.reflect.Method; import java.util.Arrays; import java.util.logging.Level; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.reflect.MethodSignature; @Aspect public class ExceptionInterceptor { private static final java.util.logging.Logger LOGGER = java.util.logging.Logger.getLogger(ExceptionInterceptor.class.getName()); @Around("execution(* * (..))" + " && @annotation(test.Audit)" ) public Object intercept(final ProceedingJoinPoint point) throws Throwable { final Method method = MethodSignature.class.cast(point.getSignature()).getMethod(); String mName = method.getName(); String cName = method.getDeclaringClass().getSimpleName(); LOGGER.log(Level.INFO, "Entering {0}:{1}", new Object[]{cName, mName}); Object out = null; try { out = point.proceed(); } catch (Throwable t) { logExceptions(t, point); } LOGGER.log(Level.INFO, "Exiting {0}:{1}", new Object[]{cName, mName}); return out; } private void logExceptions(Throwable t, final ProceedingJoinPoint point) { final Method method = MethodSignature.class.cast(point.getSignature()).getMethod(); String mName = method.getName(); String cName = method.getDeclaringClass().getSimpleName(); Object[] params = point.getArgs(); StringBuilder sb = new StringBuilder(); sb.append("Exception caught for ["); sb.append(cName); sb.append("."); sb.append(mName); for (int i = 0; i < params.length; i++) { Object param = params[i]; sb.append("\n"); sb.append(" [Arg=").append(i); if (param != null) { String type = param.getClass().getSimpleName(); sb.append(", ").append(type); // Handle Object Array (Policy Override) if (param instanceof Object[]) { sb.append("=").append(Arrays.toString((Object[]) param)); } else { sb.append("=").append(param.toString()); } } else { sb.append(", null"); } sb.append("]"); sb.append("\n"); } LOGGER.log(Level.SEVERE, sb.toString(), t); } } 

Cómo usarlo

 @Audit public void testMethod(Int a,int b, String c){ } 

Comstackciones de Maven Comstackr

   org.aspectj aspectjrt 1.8.7  

Tejeduría

   com.jcabi jcabi-maven-plugin   compile  ajc