web-dev-qa-db-ja.com

AspectJでAOPを使用してログを記録する方法

次のように、すべてのパブリックメソッドに「トレース」メッセージを追加したいと思います。

public void foo(s:String、n:int){// logはlog4jロガーまたは他のライブラリ
 log.trace(String.format( "sでfooを入力:%s、n:%d "、s、n))
 ... 
 log.trace(" Exit foo ")
}

今、私はそれらすべてを追加したいlog.trace AOP(およびバイトコードインストルメンテーション)を使用して自動的にメソッドに追加します。 AspectJについて考えています。それは理にかなっていますか?オープンソースを知っていますか?

27
Michael

パブリックメソッドの実行をキャプチャする単純な側面を作成しました。このAspectJコードの中核はポイントカット定義です:

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:In Action をお勧めします。

28
Pedro Ghilardi

@Loggable 注釈と jcabi-aspects からのAspectJアスペクトは、すぐに使えるメカニズムです(私は開発者です):

@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に送られます。詳細については this post を確認してください。

26
yegor256

さまざまなポイントカットを使用して要件を作成できます。これは ドキュメント に役立ちます。

ストレート フォワードソリューション

4
nidhin

このオープンソースを試すことができます http://code.google.com/p/perfspy/ 。 PerfSpyは、ランタイムロギング、パフォーマンスモニタリング、およびコード検査ツールです。 ApsectJを使用して、実行時にアプリケーションコードを調整し、すべてのメソッドの実行時間とその入力パラメーターと値を記録します。 UIアプリケーションがあり、メソッド呼び出しとその入力および戻り値をツリーとして表示できます。これにより、パフォーマンスのボトルネックを特定し、複雑なコードフローを理解できます。

1
user2695490

これは、メソッドの開始、終了、および例外のログを記録するための私の簡単な実装です。

注釈

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> 
0
Raghu K Nair