春のパフォーマンスログ

Spring Performance Logging

1. 概要

このチュートリアルでは、SpringFrameworkがパフォーマンス監視のために提供するいくつかの基本的なオプションについて説明します。

2. PerformanceMonitorInterceptor

メソッドの実行時間の基本的な監視機能を取得するための簡単なソリューションである、Spring AOP(アスペクト指向プログラミング)のPerformanceMonitorInterceptorクラスを利用できます。

Spring AOPを使用すると、アプリケーション内の横断的関心事、つまり1つ以上のメソッドの実行をインターセプトするコードを定義して、機能を追加できます。

PerformanceMonitorInterceptorクラスは、同時に実行される任意のカスタムメソッドに関連付けることができるインターセプターです。 このクラスは、StopWatchインスタンスを使用して、メソッド実行の開始時刻と終了時刻を決定します。

監視する2つのメソッドを使用して、単純なPersonクラスとPersonServiceクラスを作成しましょう。

public class Person {
    private String lastName;
    private String firstName;
    private LocalDate dateOfBirth;

    // standard constructors, getters, setters
}
public class PersonService {

    public String getFullName(Person person){
        return person.getLastName()+" "+person.getFirstName();
    }

    public int getAge(Person person){
        Period p = Period.between(person.getDateOfBirth(), LocalDate.now());
        return p.getYears();
    }
}

Springモニタリングインターセプターを使用するには、ポイントカットとアドバイザーを定義する必要があります。

@Configuration
@EnableAspectJAutoProxy
@Aspect
public class AopConfiguration {

    @Pointcut(
      "execution(public String com.example.performancemonitor.PersonService.getFullName(..))"
    )
    public void monitor() { }

    @Bean
    public PerformanceMonitorInterceptor performanceMonitorInterceptor() {
        return new PerformanceMonitorInterceptor(true);
    }

    @Bean
    public Advisor performanceMonitorAdvisor() {
        AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
        pointcut.setExpression("com.example.performancemonitor.AopConfiguration.monitor()");
        return new DefaultPointcutAdvisor(pointcut, performanceMonitorInterceptor());
    }

    @Bean
    public Person person(){
        return new Person("John","Smith", LocalDate.of(1980, Month.JANUARY, 12));
    }

    @Bean
    public PersonService personService(){
        return new PersonService();
    }
}

ポイントカットには、インターセプトするメソッドを識別する式が含まれています。この場合は、PersonServiceクラスのgetFullName()メソッドです。

performanceMonitorInterceptor() Beanを構成した後、インターセプターをポイントカットに関連付ける必要があります。 これは、上記の例に示すように、アドバイザーを介して実現されます。

最後に、@EnableAspectJAutoProxyアノテーションにより、BeanのAspectJ supportが有効になります。 簡単に言えば、AspectJは、@Pointcutのような便利なアノテーションを通じてSpringAOPの使用を容易にするために作成されたライブラリです。

構成を作成した後、set the log level of the interceptor class to TRACEを実行する必要があります。これは、メッセージをログに記録するレベルであるためです。

たとえば、Jog4jを使用すると、log4j.propertiesファイルを使用してこれを実現できます。

log4j.logger.org.springframework.aop.interceptor.PerformanceMonitorInterceptor=TRACE, stdout

getAge()メソッドを実行するたびに、コンソールログにTRACEメッセージが表示されます。

2017-01-08 19:19:25 TRACE
  PersonService:66 - StopWatch
  'com.example.performancemonitor.PersonService.getFullName':
  running time (millis) = 10

3. カスタムパフォーマンスモニタリングインターセプター

more control over the way the performance monitoringを実行したい場合は、独自のカスタムインターセプターを実装できます。

このために、AbstractMonitoringInterceptorクラスを拡張し、invokeUnderTrace()メソッドをオーバーライドして、メソッドの開始、終了、期間、およびメソッドの実行が10ミリ秒を超えた場合の警告をログに記録しましょう。

public class MyPerformanceMonitorInterceptor extends AbstractMonitoringInterceptor {

    public MyPerformanceMonitorInterceptor() {
    }

    public MyPerformanceMonitorInterceptor(boolean useDynamicLogger) {
            setUseDynamicLogger(useDynamicLogger);
    }

    @Override
    protected Object invokeUnderTrace(MethodInvocation invocation, Log log)
      throws Throwable {
        String name = createInvocationTraceName(invocation);
        long start = System.currentTimeMillis();
        log.info("Method " + name + " execution started at:" + new Date());
        try {
            return invocation.proceed();
        }
        finally {
            long end = System.currentTimeMillis();
            long time = end - start;
            log.info("Method "+name+" execution lasted:"+time+" ms");
            log.info("Method "+name+" execution ended at:"+new Date());

            if (time > 10){
                log.warn("Method execution longer than 10 ms!");
            }
        }
    }
}

前のセクションと同じように、カスタムインターセプターを1つ以上のメソッドに関連付けるための同じ手順に従う必要があります。

PersonServicegetAge()メソッドのポイントカットを定義し、それを作成したインターセプターに関連付けましょう。

@Pointcut("execution(public int com.example.performancemonitor.PersonService.getAge(..))")
public void myMonitor() { }

@Bean
public MyPerformanceMonitorInterceptor myPerformanceMonitorInterceptor() {
    return new MyPerformanceMonitorInterceptor(true);
}

@Bean
public Advisor myPerformanceMonitorAdvisor() {
    AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
    pointcut.setExpression("com.example.performancemonitor.AopConfiguration.myMonitor()");
    return new DefaultPointcutAdvisor(pointcut, myPerformanceMonitorInterceptor());
}

カスタムインターセプターのログレベルをINFOに設定しましょう。

log4j.logger.com.example.performancemonitor.MyPerformanceMonitorInterceptor=INFO, stdout

getAge()メソッドを実行すると、次の出力が生成されました。

2017-01-08 19:19:25 INFO PersonService:26 -
  Method com.example.performancemonitor.PersonService.getAge
  execution started at:Sun Jan 08 19:19:25 EET 2017
2017-01-08 19:19:25 INFO PersonService:33 -
  Method com.example.performancemonitor.PersonService.getAge execution lasted:50 ms
2017-01-08 19:19:25 INFO PersonService:34 -
  Method com.example.performancemonitor.PersonService.getAge
  execution ended at:Sun Jan 08 19:19:25 EET 2017
2017-01-08 19:19:25 WARN PersonService:37 -
  Method execution longer than 10 ms!

4. 結論

このクイックチュートリアルでは、Springでの簡単なパフォーマンスモニタリングを紹介しました。

いつものように、この記事の完全なソースコードはover on Githubにあります。