From 7a1b4ac907119f16653a313f30ffc7c583f8acdd Mon Sep 17 00:00:00 2001 From: Phil Winder Date: Tue, 14 Mar 2017 15:17:01 +0000 Subject: [PATCH] Add prometheus with HTTP interceptor. --- pom.xml | 16 +++++-- .../config/PrometheusAutoConfiguration.java | 35 ++++++++++++++ .../socks/orders/config/WebMvcConfig.java | 25 ++++++++++ .../middleware/HTTPMonitoringInterceptor.java | 48 +++++++++++++++++++ 4 files changed, 119 insertions(+), 5 deletions(-) create mode 100644 src/main/java/works/weave/socks/orders/config/PrometheusAutoConfiguration.java create mode 100644 src/main/java/works/weave/socks/orders/config/WebMvcConfig.java create mode 100644 src/main/java/works/weave/socks/orders/middleware/HTTPMonitoringInterceptor.java diff --git a/pom.xml b/pom.xml index 6d37dac..d2bc631 100644 --- a/pom.xml +++ b/pom.xml @@ -14,12 +14,13 @@ org.springframework.boot spring-boot-starter-parent - 1.4.0.RELEASE + 1.4.4.RELEASE UTF-8 1.8 + 0.0.21 @@ -38,13 +39,18 @@ io.prometheus - simpleclient - RELEASE + simpleclient_spring_boot + ${prometheus.version} io.prometheus - simpleclient_common - RELEASE + simpleclient_hotspot + ${prometheus.version} + + + io.prometheus + simpleclient_servlet + ${prometheus.version} org.springframework.data diff --git a/src/main/java/works/weave/socks/orders/config/PrometheusAutoConfiguration.java b/src/main/java/works/weave/socks/orders/config/PrometheusAutoConfiguration.java new file mode 100644 index 0000000..bdbdc37 --- /dev/null +++ b/src/main/java/works/weave/socks/orders/config/PrometheusAutoConfiguration.java @@ -0,0 +1,35 @@ +package works.weave.socks.orders.config; + +import io.prometheus.client.exporter.MetricsServlet; +import io.prometheus.client.hotspot.DefaultExports; +import io.prometheus.client.spring.boot.SpringBootMetricsCollector; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.actuate.endpoint.PublicMetrics; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.web.servlet.ServletRegistrationBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import java.util.Collection; + +@Configuration +@ConditionalOnClass(SpringBootMetricsCollector.class) +class PrometheusAutoConfiguration { + @Bean + @ConditionalOnMissingBean(SpringBootMetricsCollector.class) + SpringBootMetricsCollector springBootMetricsCollector(Collection publicMetrics) { + SpringBootMetricsCollector springBootMetricsCollector = new SpringBootMetricsCollector + (publicMetrics); + springBootMetricsCollector.register(); + return springBootMetricsCollector; + } + + @Bean + @ConditionalOnMissingBean(name = "prometheusMetricsServletRegistrationBean") + ServletRegistrationBean prometheusMetricsServletRegistrationBean(@Value("${prometheus.metrics" + + ".path:/metrics}") String metricsPath) { + DefaultExports.initialize(); + return new ServletRegistrationBean(new MetricsServlet(), metricsPath); + } +} diff --git a/src/main/java/works/weave/socks/orders/config/WebMvcConfig.java b/src/main/java/works/weave/socks/orders/config/WebMvcConfig.java new file mode 100644 index 0000000..c458516 --- /dev/null +++ b/src/main/java/works/weave/socks/orders/config/WebMvcConfig.java @@ -0,0 +1,25 @@ +package works.weave.socks.orders.config; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; +import works.weave.socks.orders.middleware.HTTPMonitoringInterceptor; + +@Configuration +public class WebMvcConfig extends WebMvcConfigurerAdapter { + @Autowired + private HTTPMonitoringInterceptor httpMonitoringInterceptor; + + @Bean + HTTPMonitoringInterceptor httpMonitoringInterceptor() { + return new HTTPMonitoringInterceptor(); + } + + @Override + public void addInterceptors(InterceptorRegistry registry) { + registry.addInterceptor(httpMonitoringInterceptor) + .addPathPatterns("/**"); + } +} diff --git a/src/main/java/works/weave/socks/orders/middleware/HTTPMonitoringInterceptor.java b/src/main/java/works/weave/socks/orders/middleware/HTTPMonitoringInterceptor.java new file mode 100644 index 0000000..e8c5298 --- /dev/null +++ b/src/main/java/works/weave/socks/orders/middleware/HTTPMonitoringInterceptor.java @@ -0,0 +1,48 @@ +package works.weave.socks.orders.middleware; + +import io.prometheus.client.Histogram; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.web.servlet.HandlerInterceptor; +import org.springframework.web.servlet.ModelAndView; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +public class HTTPMonitoringInterceptor implements HandlerInterceptor { + static final Histogram requestLatency = Histogram.build() + .name("request_duration_seconds") + .help("Request duration in seconds.") + .labelNames("service", "method", "route", "status_code") + .register(); + + private static final String startTimeKey = "startTime"; + + @Value("${spring.application.name:orders}") + private String serviceName; + + @Override + public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse + httpServletResponse, Object o) throws Exception { + httpServletRequest.setAttribute(startTimeKey, System.nanoTime()); + return true; + } + + @Override + public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse + httpServletResponse, Object o, ModelAndView modelAndView) throws Exception { + long start = (long) httpServletRequest.getAttribute(startTimeKey); + long elapsed = System.nanoTime() - start; + double seconds = (double) elapsed / 1000000000.0; + requestLatency.labels( + serviceName, + httpServletRequest.getMethod(), + httpServletRequest.getServletPath(), + Integer.toString(httpServletResponse.getStatus()) + ).observe(seconds); + } + + @Override + public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse + httpServletResponse, Object o, Exception e) throws Exception { + } +}