-
Notifications
You must be signed in to change notification settings - Fork 830
Add AOP-based spring method timing metrics, servlet filter for path-based metrics. #168
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
9a106b1
adfd0f7
1500059
a6f6fe3
a825ca3
4b26778
1e687e0
c35e04a
f2da166
61e7e16
36baca8
d993c11
3638348
cd16640
b5b9cca
4de8105
e515ea3
356a341
215162d
82f5b49
50a1306
42584f4
985cc89
eeaf05a
389f9ef
6f80d07
2d17f74
98caad8
9f3738b
c332880
1b53302
9cde3ff
1819ad5
f8a2049
af8488a
f89d8e5
2d1f9c2
44d7395
9f49f74
de13c11
9e65362
d34b3cf
9ea5b8f
1344b78
088549a
e25f240
1d92c00
a9388e7
b977ca3
3737d2b
27cf198
16ccf33
3af1754
c640110
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,181 @@ | ||
| package io.prometheus.client.filter; | ||
|
|
||
| import io.prometheus.client.Histogram; | ||
|
|
||
| import javax.servlet.Filter; | ||
| import javax.servlet.FilterChain; | ||
| import javax.servlet.FilterConfig; | ||
| import javax.servlet.ServletException; | ||
| import javax.servlet.ServletRequest; | ||
| import javax.servlet.ServletResponse; | ||
| import javax.servlet.http.HttpServletRequest; | ||
| import java.io.IOException; | ||
|
|
||
| /** | ||
| * The MetricsFilter class exists to provide a high-level filter that enables tunable collection of metrics for Servlet | ||
| * performance. | ||
| * | ||
| * The Histogram name itself is required, and configured with a {@code metric-name} init parameter. | ||
| * | ||
| * The help parameter, configured with the {@code help} init parameter, is not required but strongly recommended. | ||
| * | ||
| * By default, this filter will provide metrics that distinguish only 1 level deep for the request path | ||
| * (including servlet context path), but can be configured with the {@code path-components} init parameter. Any number | ||
| * provided that is less than 1 will provide the full path granularity (warning, this may affect performance). | ||
| * | ||
| * The Histogram buckets can be configured with a {@code buckets} init parameter whose value is a comma-separated list | ||
| * of valid {@code double} values. | ||
| * | ||
| * {@code | ||
| * <filter> | ||
| * <filter-name>prometheusFilter</filter-name> | ||
| * <filter-class>net.cccnext.ssp.portal.spring.filter.PrometheusMetricsFilter</filter-class> | ||
| * <init-param> | ||
| * <param-name>metric-name</param-name> | ||
| * <param-value>webapp_metrics_filter</param-value> | ||
| * </init-param> | ||
| * <init-param> | ||
| * <param-name>help</param-name> | ||
| * <param-value>The time taken fulfilling servlet requests</param-value> | ||
| * </init-param> | ||
| * <init-param> | ||
| * <param-name>buckets</param-name> | ||
| * <param-value>0.005,0.01,0.025,0.05,0.075,0.1,0.25,0.5,0.75,1,2.5,5,7.5,10</param-value> | ||
| * </init-param> | ||
| * <init-param> | ||
| * <param-name>path-components</param-name> | ||
| * <param-value>0</param-value> | ||
| * </init-param> | ||
| * </filter> | ||
| * } | ||
| * | ||
| * @author Andrew Stuart <[email protected]> | ||
| */ | ||
| public class MetricsFilter implements Filter { | ||
| static final String PATH_COMPONENT_PARAM = "path-components"; | ||
| static final String HELP_PARAM = "help"; | ||
| static final String METRIC_NAME_PARAM = "metric-name"; | ||
| static final String BUCKET_CONFIG_PARAM = "buckets"; | ||
|
|
||
| private Histogram histogram = null; | ||
|
|
||
| // Package-level for testing purposes. | ||
| int pathComponents = 1; | ||
| private String metricName = null; | ||
| private String help = "The time taken fulfilling servlet requests"; | ||
| private double[] buckets = null; | ||
|
|
||
| public MetricsFilter() {} | ||
|
|
||
| public MetricsFilter( | ||
| String metricName, | ||
| String help, | ||
| Integer pathComponents, | ||
| double[] buckets | ||
| ) throws ServletException { | ||
| this.metricName = metricName; | ||
| this.buckets = buckets; | ||
| if (help != null) { | ||
| this.help = help; | ||
| } | ||
| if (pathComponents != null) { | ||
| this.pathComponents = pathComponents; | ||
| } | ||
| this.init(null); | ||
| } | ||
|
|
||
| private boolean isEmpty(String s) { | ||
| return s == null || s.length() == 0; | ||
| } | ||
|
|
||
| private String getComponents(String str) { | ||
| if (str == null || pathComponents < 1) { | ||
| return str; | ||
| } | ||
| int count = 0; | ||
| int i = -1; | ||
| do { | ||
| i = str.indexOf("/", i + 1); | ||
| if (i < 0) { | ||
| // Path is longer than specified pathComponents. | ||
| return str; | ||
| } | ||
| count++; | ||
| } while (count <= pathComponents); | ||
|
|
||
| return str.substring(0, i); | ||
| } | ||
|
|
||
| @Override | ||
| public void init(FilterConfig filterConfig) throws ServletException { | ||
| Histogram.Builder builder = Histogram.build() | ||
| .labelNames("path", "method"); | ||
|
|
||
| if (filterConfig == null && isEmpty(metricName)) { | ||
| throw new ServletException("No configuration object provided, and no metricName passed via constructor"); | ||
| } | ||
|
|
||
| if (filterConfig != null) { | ||
| if (isEmpty(metricName)) { | ||
| metricName = filterConfig.getInitParameter(METRIC_NAME_PARAM); | ||
| if (isEmpty(metricName)) { | ||
| throw new ServletException("Init parameter \"" + METRIC_NAME_PARAM + "\" is required; please supply a value"); | ||
| } | ||
| } | ||
|
|
||
| if (!isEmpty(filterConfig.getInitParameter(HELP_PARAM))) { | ||
| help = filterConfig.getInitParameter(HELP_PARAM); | ||
| } | ||
|
|
||
| // Allow overriding of the path "depth" to track | ||
| if (!isEmpty(filterConfig.getInitParameter(PATH_COMPONENT_PARAM))) { | ||
| pathComponents = Integer.valueOf(filterConfig.getInitParameter(PATH_COMPONENT_PARAM)); | ||
| } | ||
|
|
||
| // Allow users to override the default bucket configuration | ||
| if (!isEmpty(filterConfig.getInitParameter(BUCKET_CONFIG_PARAM))) { | ||
| String[] bucketParams = filterConfig.getInitParameter(BUCKET_CONFIG_PARAM).split(","); | ||
| buckets = new double[bucketParams.length]; | ||
|
|
||
| for (int i = 0; i < bucketParams.length; i++) { | ||
| buckets[i] = Double.parseDouble(bucketParams[i]); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| if (buckets != null) { | ||
| builder = builder.buckets(buckets); | ||
| } | ||
|
|
||
| histogram = builder | ||
| .help(help) | ||
| .name(metricName) | ||
| .register(); | ||
| } | ||
|
|
||
| @Override | ||
| public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { | ||
| if (!(servletRequest instanceof HttpServletRequest)) { | ||
| filterChain.doFilter(servletRequest, servletResponse); | ||
| return; | ||
| } | ||
|
|
||
| HttpServletRequest request = (HttpServletRequest) servletRequest; | ||
|
|
||
| String path = request.getRequestURI(); | ||
|
||
|
|
||
| Histogram.Timer timer = histogram | ||
| .labels(getComponents(path), request.getMethod()) | ||
| .startTimer(); | ||
|
|
||
| try { | ||
| filterChain.doFilter(servletRequest, servletResponse); | ||
| } finally { | ||
| timer.observeDuration(); | ||
| } | ||
| } | ||
|
|
||
| @Override | ||
| public void destroy() { | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you provide an example usage here, and in the README.md?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, no problem. The root README, or create a new README in the
simpleclient_servletpackage?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The root README.
The others should have examples too.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like I was a bit behind
master, so I'm rebasing. I'll add the examples there as well.