Leistungsüberwachung mit Spring Boot
In diesem Beitrag möchte ich einige interessante Funktionen zeigen, die Spring Boot für die Leistungsüberwachung bietet.
Sobald die Anwendung zu skalieren beginnt, wird die Leistung zur obersten Priorität. Wir überoptimieren die Anwendung, um die Einfachheit zu verlieren. So funktioniert Softwareentwicklung. In einem Produktionsszenario überwachen wir unsere Anwendung auf Leistung. Da die meisten Anwendungen in die Cloud verlagert werden, ist es wichtig, die Anwendung zu überwachen und die Leistung ständig zu verbessern.
Wenn Sie einen Federantrieb verwendet haben, bietet dieser eine Reihe von Statistiken zur Überwachung. Zuvor habe ich dieses Thema Federantrieb behandelt.
Anschließend werden wir einige verschiedene Funktionen von Spring Boot behandeln. Wir werden über CustomizableTraceInterceptor sprechen , PerformanceMonitorInterceptor und CommonsRequestLoggingFilter .
Verwendung von CustomizableTraceInterceptor
Sie können CustomizableTraceInterceptor als Bean
hinzufügen und verwenden Sie diese Bean als Ratgeber für die Ausdrücke, die Sie abfangen möchten. Grundsätzlich ermöglicht uns dieser Interceptor, die Methodenaufrufe abzufangen und benutzerdefinierte Protokollmeldungen hinzuzufügen.
Um dies im Arbeitsbeispiel zu zeigen, werden wir die Repository-Timings verfolgen. Erstellen Sie zunächst eine Klasse, die CustomizableTraceInterceptor
erweitert wie folgt:
package com.abccompany.home.performance;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.commons.logging.Log;
import org.springframework.aop.framework.AopProxyUtils;
import org.springframework.aop.interceptor.CustomizableTraceInterceptor;
import org.springframework.data.jpa.repository.support.SimpleJpaRepository;
public class RepositoryMethodInterceptor extends CustomizableTraceInterceptor
{
@Override
protected Class> getClassForLogging(Object target)
{
Class> classForLogging = super.getClassForLogging(target);
if (SimpleJpaRepository.class.equals(classForLogging))
{
Class>[] interfaces = AopProxyUtils.proxiedUserInterfaces(target);
if (interfaces.length > 0)
{
return interfaces[0];
}
}
return classForLogging;
}
protected void writeToLog(Log logger, String message, Throwable ex)
{
if (ex != null)
{
logger.info(message, ex);
}
else
{
logger.info(message);
}
}
protected boolean isInterceptorEnabled(MethodInvocation invocation, Log logger)
{
return true;
}
}
Ebenso werde ich gleich erklären, was diese Klasse macht. Wir brauchen eine @Bean
die diesen Interceptor verwenden, um Repository-Methoden abzufangen. Der Code dafür sieht wie folgt aus:
package com.abccompany.home.performance;
import org.springframework.aop.Advisor;
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class TraceLoggerConfig
{
@Bean
public RepositoryMethodInterceptor repositoryMethodInterceptor()
{
RepositoryMethodInterceptor repositoryMethodInterceptor = new RepositoryMethodInterceptor();
repositoryMethodInterceptor.setHideProxyClassNames(true);
repositoryMethodInterceptor.setUseDynamicLogger(false);
repositoryMethodInterceptor.setExitMessage("Executed $[methodName] in $[invocationTime] " +
"ms");
return repositoryMethodInterceptor;
}
@Bean
public Advisor advisor()
{
AspectJExpressionPointcut aspectJExpressionPointcut = new AspectJExpressionPointcut();
aspectJExpressionPointcut.setExpression("execution(public * com.abccompany.home" +
".repositories.*Repository+.*(..))");
return new DefaultPointcutAdvisor(aspectJExpressionPointcut, repositoryMethodInterceptor());
}
}
Wenn wir uns nun diese Konfiguration ansehen, erstellt sie eine Bean für die Verwendung von RepositoryMethodInterceptor
das ist eine Unterklasse von CustomizableTraceInterceptor
. Sie können sehen, dass wir eine Exit-Nachricht verwendet haben, um das Timing zu protokollieren, das die Repository-Methode in dieser Bean verwendet hat.
repositoryMethodInterceptor.setExitMessage("Executed $[methodName] in $[invocationTime] " + "ms");
AspectJExpression erstellt einen Ausdruck, für den das Abfangen von Paketen erfolgen soll. Die Klasse RepositoryMethodInterceptor
macht ein paar nützliche Dinge. Erstens hilft es uns, die Klasseninformationen zu verfolgen von Repository
Klassen. Zweitens protokolliert es die Nachricht in unserer Logdatei. Sobald Sie die Anwendung ausführen, sehen Sie die folgenden Protokollmeldungen:
2020-05-24 19:08:04.870 INFO 14724 --- [nio-8443-exec-9] c.r.h.p.RepositoryMethodInterceptor : Entering method 'findUserByEmail' of class [com.abccompany.home.repositories.UserdataRepository] Hibernate: select userdata0_.id as id1_4_, userdata0_.email as email2_4_, userdata0_.firstname as firstnam3_4_, userdata0_.guid as guid4_4_, userdata0_.lastname as lastname5_4_, userdata0_.middlename as middlena6_4_, userdata0_.confirmpassword as confirmp7_4_, userdata0_.passwordtxt as password8_4_, userdata0_.phonenumber as phonenum9_4_, userdata0_.role as role10_4_ from userdata userdata0_ where userdata0_.email=?
2020-05-24 19:08:04.872 INFO 14724 --- [nio-8443-exec-9] c.r.h.p.RepositoryMethodInterceptor : Executed findUserByEmail in 2 ms
2020-05-24 19:08:04.872 INFO 14724 --- [nio-8443-exec-9] c.r.h.p.RepositoryMethodInterceptor : Entering method 'findAll' of class [com.abccompany.home.repositories.FeedbackRepository] Hibernate: select feedback0_.id as id1_1_, feedback0_.createdon as createdo2_1_, feedback0_.fromdate as fromdate3_1_, feedback0_.guid as guid4_1_, feedback0_.rating as rating5_1_, feedback0_.rentalpropertyid as rentalpr8_1_, feedback0_.review as review6_1_, feedback0_.todate as todate7_1_, feedback0_.userid as userid9_1_ from feedback feedback0_
2020-05-24 19:08:04.876 INFO 14724 --- [nio-8443-exec-9] c.r.h.p.RepositoryMethodInterceptor : Executed findAll in 4 ms
Verwendung der PerformanceMonitorInterceptor-Funktion
Also, um PerformanceMonitorInterceptor
zu verwenden , erstellen wir eine Konfigurationsklasse und fügen eine Bean hinzu, die PerformanceMonitorInterceptor
erstellt . AspectJExpressionPointcut
zeigt auf den Ausdruck, der unsere Controller-Klassen auswertet.
Wie CustomizableTraceInterceptor
, werden wir eine Unterklasse haben, die erweitert wird PerformanceMonitoringInterceptor
damit wir unsere Nachrichten in der Spring Boot-Protokollierung protokollieren können. Dies sieht wie folgt aus:
package com.abccompany.home.performance;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.commons.logging.Log;
import org.springframework.aop.interceptor.PerformanceMonitorInterceptor;
public class ControllerMonitoringInterceptor extends PerformanceMonitorInterceptor
{
protected void writeToLog(Log logger, String message, Throwable ex)
{
if (ex != null)
{
logger.info(message, ex);
}
else
{
logger.info(message);
}
}
protected boolean isInterceptorEnabled(MethodInvocation invocation, Log logger)
{
return true;
}
}
Wir werden eine Bean für ControllerMonitoringInterceptor
erstellen . Diese Bean wird Teil unserer Logger-Konfiguration sein, die auch AspectJExpression
auswertet für Controller-Klassen. Daher sieht es wie folgt aus:
package com.abccompany.home.performance;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.aop.Advisor;
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
import org.springframework.aop.interceptor.PerformanceMonitorInterceptor;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
@Configuration
@EnableAspectJAutoProxy
@Aspect
public class ControllerLoggerConfig
{
@Pointcut("execution(* com.abccompany.home.controllers.*Controller+.*(..))")
public void monitor()
{
}
@Bean
public ControllerMonitoringInterceptor controllerMonitoringInterceptor()
{
return new ControllerMonitoringInterceptor();
}
@Bean
public Advisor advisorPerformance()
{
AspectJExpressionPointcut aspectJExpressionPointcut = new AspectJExpressionPointcut();
aspectJExpressionPointcut.setExpression("com.abccompany.home.performance" +
".ControllerLoggerConfig.monitor()");
return new DefaultPointcutAdvisor(aspectJExpressionPointcut,
controllerMonitoringInterceptor());
}
}
Wenn wir nun die Anwendung ausführen, zeigen die Protokollnachrichten Latenzen für Controller-Klassen.
2020-05-24 20:12:09.237 INFO 9280 --- [nio-8443-exec-6] c.r.h.p.ControllerMonitoringInterceptor : StopWatch 'com.abccompany.home.controllers.LoginController.signin': running time (millis) = 0
2020-05-24 20:12:18.263 INFO 9280 --- [nio-8443-exec-2] c.r.h.p.ControllerMonitoringInterceptor : StopWatch 'com.abccompany.home.controllers.MainController.home': running time (millis) = 43
2020-05-24 20:12:20.025 INFO 9280 --- [nio-8443-exec-9] c.r.h.p.ControllerMonitoringInterceptor : StopWatch 'com.abccompany.home.controllers.MainController.logout': running time (millis) = 12
2020-05-24 20:12:20.042 INFO 9280 --- [nio-8443-exec-5] c.r.h.p.ControllerMonitoringInterceptor : StopWatch 'com.abccompany.home.controllers.LoginController.login': running time (millis) = 0
Verwendung von CommonsRequestLoggingFilter
Darüber hinaus bietet Spring Boot eine nützliche Funktion zum Protokollieren eingehender Anfragen. Dies hilft bei der Überwachung der Anwendung und um zu sehen, wie die Anfragen kommen. Um diese Funktion zu verwenden, erstellen wir einen @Configuration
Klasse RequestLoggingFilter wie folgt:
package com.abccompany.home.performance;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.filter.AbstractRequestLoggingFilter;
import org.springframework.web.filter.CommonsRequestLoggingFilter;
import javax.servlet.http.HttpServletRequest;
@Configuration
public class RequestLoggingFilter extends AbstractRequestLoggingFilter
{
@Bean
public CommonsRequestLoggingFilter requestLoggingFilterConfig()
{
CommonsRequestLoggingFilter commonsRequestLoggingFilter = new CommonsRequestLoggingFilter();
commonsRequestLoggingFilter.setIncludeClientInfo(true);
commonsRequestLoggingFilter.setIncludeQueryString(true);
commonsRequestLoggingFilter.setIncludePayload(true);
return commonsRequestLoggingFilter;
}
@Override
protected void beforeRequest (HttpServletRequest request, String message)
{
logger.info(message);
}
@Override
protected void afterRequest (HttpServletRequest request, String message)
{
logger.info(message);
}
}
Sobald wir dies hinzugefügt haben, sehen wir beforeRequest
und afterRequest
Meldungen im Protokoll wie folgt:
2020-05-24 21:07:15.161 INFO 11984 --- [nio-8443-exec-1] gFilter$$EnhancerBySpringCGLIB$$cb4fdaab : Before request [uri=/css/bootstrap.min.css]
2020-05-24 21:07:15.171 INFO 11984 --- [nio-8443-exec-2] gFilter$$EnhancerBySpringCGLIB$$cb4fdaab : Before request [uri=/js/jquery.min.js]
2020-05-24 21:07:15.203 INFO 11984 --- [nio-8443-exec-7] gFilter$$EnhancerBySpringCGLIB$$cb4fdaab : Before request [uri=/js/bootstrap.min.js]
2020-05-24 21:07:15.290 INFO 11984 --- [nio-8443-exec-7] gFilter$$EnhancerBySpringCGLIB$$cb4fdaab : After request [uri=/js/bootstrap.min.js]
2020-05-24 21:07:15.306 INFO 11984 --- [nio-8443-exec-2] gFilter$$EnhancerBySpringCGLIB$$cb4fdaab : After request [uri=/js/jquery.min.js]
2020-05-24 21:07:15.318 INFO 11984 --- [nio-8443-exec-1] gFilter$$EnhancerBySpringCGLIB$$cb4fdaab : After request [uri=/css/bootstrap.min.css]
Schlussfolgerung
Abschließend habe ich drei Funktionen zur Leistungsüberwachung gezeigt – CustomizableTraceInterceptor , PerformanceMonitorInterceptor und CommonsRequestLoggingFilter um nützliche Leistungsmetriken zu protokollieren.
Referenzen
- Spring Framework-Funktionen
- CommonsRequestLoggingFilter