web-dev-qa-db-fra.com

Comment utiliser AOP avec AspectJ pour la journalisation?

J'aimerais ajouter des messages "trace" à toutes mes méthodes publiques comme suit:

public void foo (s: String, n: int) {// log est un enregistreur log4j ou toute autre bibliothèque 
 log.trace (String.format ("Entrez foo avec s:% s, n:% d", s, n)) 
 ...
 log.trace ("Exit foo") 
}

Maintenant, j'aimerais ajouter tous ces log.trace à mes méthodes automatiquement avec AOP (et l'instrumentation du code d'octet). Je pense à AspectJ. Est-ce que ça fait du sens? Connaissez-vous des logiciels libres, qui font exactement cela?

26
Michael

J'ai créé un aspect simple pour capturer l'exécution de méthodes publiques. Le noyau de ce code AspectJ est la définition du pointcut:

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

Nous capturons ici toutes les méthodes publiques avec tout type de retour, sur tout package et toute classe, avec un nombre quelconque de paramètres.

L'exécution des conseils peut être visualisée sur l'extrait de code ci-dessous:

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());
}

Ce conseil utilise thisJoinPoint pour obtenir la signature et les arguments de la méthode. Et c'est tout. Voici le code d'aspect:

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());
}

Pour des exemples plus complexes, je recommanderais le livre AspectJ: In Action .

25
Pedro Ghilardi

@Loggable annotation et un aspect AspectJ de jcabi-aspects est un mécanisme prêt à l'emploi pour vous (je suis un développeur):

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

Pour consigner à la fois l'entrée et la sortie, selon les exigences de la question:

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

Tous les journaux vont à SLF4J. Vérifiez ce post pour plus de détails.

24
yegor256

Vous pouvez utiliser différents pointcuts pour répondre à vos besoins. Cette documentation vous aidera.

Droit Solution avancée

4
nidhin

Vous pouvez essayer cet open source http://code.google.com/p/perfspy/ . PerfSpy est un outil de journalisation, de surveillance des performances et d’inspection de code à l’exécution. Il utilise ApsectJ pour tisser votre code d'application au moment de l'exécution et enregistre l'heure d'exécution de chaque méthode, ainsi que ses paramètres et valeurs d'entrée. Il possède une application d'interface utilisateur dans laquelle vous pouvez afficher les invocations de méthodes et leurs valeurs d'entrée et de retour sous forme d'arborescence. Grâce à lui, vous pouvez repérer les goulots d'étranglement liés aux performances et comprendre le flux de code complexe. 

1
user2695490

Voici mon implémentation simple pour consigner les entrées, les sorties et les exceptions 

Annotation 

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 {

}

Intercepteur

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);

    }
}

Comment l'utiliser 

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

Dépendances Maven Compiler 

    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjrt</artifactId>
        <version>1.8.7</version>
    </dependency> 

Tissage 

        <plugin>
            <groupId>com.jcabi</groupId>
            <artifactId>jcabi-maven-plugin</artifactId>
            <executions>
                <execution>
                    <phase>compile</phase>
                    <goals>
                        <goal>ajc</goal>
                    </goals>
                </execution>
            </executions>
        </plugin> 
0
Raghu K Nair