Как использовать AOP с AspectJ для ведения журнала?

Я хотел бы добавить сообщения "трассировки" ко всем моим общедоступным методам следующим образом:

public void foo(s:String, n:int) { // log is a log4j logger or any other library
  log.trace(String.format("Enter foo with s: %s, n: %d", s, n))
  ...
  log.trace("Exit foo") 
}

Теперь я хотел бы добавить все эти log.trace к моим методам автоматически с помощью AOP (и инструментария байтового кода). Я думаю о AspectJ. Имеет ли это смысл? Вы знаете какой-либо open-source, который делает именно это?

Ответ 1

Я создал простой аспект, чтобы зафиксировать выполнение публичных методов. Ядром этого кода AspectJ является определение pointcut:

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

Этот совет использует thisJoinPoint, чтобы получить подпись и аргументы метода. И это так. Вот код аспекта:

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

Для более сложных примеров я бы рекомендовал книгу AspectJ: В действии.

Ответ 2

@Loggable аннотация и аспект AspectJ из jcabi-аспекты - это готовый механизм для вас (я разработчик):

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

Чтобы зарегистрировать оба входа и выхода, в соответствии с требованиями к запросу:

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

Все журналы идут на SLF4J. Чтобы узнать подробности, этот пост.

Ответ 4

Вы можете попробовать этот открытый источник http://code.google.com/p/perfspy/. PerfSpy - это журнал выполнения, контроль производительности и проверка кода. Он использует ApsectJ, чтобы сплести код приложения во время выполнения и регистрирует время выполнения каждого метода и его входных параметров и значений. Он имеет приложение пользовательского интерфейса, в котором вы можете просматривать вызовы метода и их значения ввода и возврата в виде деревьев. С его помощью вы можете выявить узкие места в производительности и понять сложный поток кода.

Ответ 5

Вот моя простая реализация для входа в журнал, выхода и регистрации Исключения из методов

Аннотации

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 {

}

Перехватчик

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

    }
}

Как использовать его

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

Зависимости Maven Компилировать

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

Плетение

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