Как использовать 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. Чтобы узнать подробности, этот пост.
Ответ 3
Вы можете использовать разные точки, чтобы выполнить свое требование. Эта документация поможет вам.
Прямо переадресация
Ответ 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>