Registro de desempenho da primavera

Registro de desempenho da primavera

1. Visão geral

Neste tutorial, veremos algumas opções básicas que o Spring Framework oferece para monitoramento de desempenho.

2. PerformanceMonitorInterceptor

Uma solução simples para obter funcionalidade básica de monitoramento para o tempo de execução de nossos métodos, podemos fazer uso da classePerformanceMonitorInterceptor do Spring AOP (Aspect Oriented Programming).

O Spring AOP permite definir preocupações transversais em aplicativos, ou seja, código que intercepta a execução de um ou mais métodos, a fim de adicionar funcionalidade extra.

A classePerformanceMonitorInterceptor é um interceptor que pode ser associado a qualquer método personalizado a ser executado ao mesmo tempo. Esta classe usa uma instânciaStopWatch para determinar a hora de início e de término da execução do método.

Vamos criar uma classePerson simples e uma classePersonService com dois métodos que monitoraremos:

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

Para fazer uso do interceptador de monitoramento Spring, precisamos definir um pointcut e um orientador:

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

O pointcut contém uma expressão que identifica os métodos que queremos que sejam interceptados - em nosso caso, o métodogetFullName() da classePersonService.

Depois de configurar o beanperformanceMonitorInterceptor(), precisamos associar o interceptor ao pointcut. Isso é conseguido através de um consultor, conforme mostrado no exemplo acima.

Finalmente, a anotação@EnableAspectJAutoProxy ativaAspectJ support para nossos beans. Simplificando, AspectJ é uma biblioteca criada para tornar o uso do Spring AOP mais fácil por meio de anotações convenientes como@Pointcut.

Depois de criar a configuração, precisamosset the log level of the interceptor class to TRACE, pois este é o nível em que ele registra as mensagens.

Por exemplo, usando Jog4j, podemos fazer isso por meio do arquivolog4j.properties:

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

Para cada execução do métodogetAge(), veremos a mensagemTRACE no log do console:

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

3. Interceptor de monitoramento de desempenho personalizado

Se quisermos quemore control over the way the performance monitoring esteja pronto, podemos implementar nosso próprio interceptor personalizado.

Para isso, vamos estender a classeAbstractMonitoringInterceptor e substituir o métodoinvokeUnderTrace() para registrar o início, o fim e a duração de um método, bem como um aviso se a execução do método durar mais de 10 ms:

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!");
            }
        }
    }
}

As mesmas etapas para associar o interceptor personalizado a um ou mais métodos, como na seção anterior, precisam ser seguidas.

Vamos definir um ponto de corte para o métodogetAge() dePersonServicee associá-lo ao interceptor que criamos:

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

Vamos definir o nível de registro paraINFO para o interceptor personalizado:

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

A execução do método getAge() produziu a seguinte saída:

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. Conclusão

Neste tutorial rápido, apresentamos o monitoramento de desempenho simples no Spring.

Como sempre, o código-fonte completo deste artigo pode ser encontradoover on Github.