Skip to content
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

Count HTTP statuses returned along with the HTTP response times #560

Merged
merged 3 commits into from
Jul 16, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package io.prometheus.client.filter;

import io.prometheus.client.Counter;
import io.prometheus.client.Histogram;

import javax.servlet.Filter;
Expand All @@ -9,13 +10,14 @@
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
* The MetricsFilter class exists to provide a high-level filter that enables tunable collection of metrics for Servlet
* performance.
*
* <p>The Histogram name itself is required, and configured with a {@code metric-name} init parameter.
* <p>The metric name itself is required, and configured with a {@code metric-name} init parameter.
*
* <p>The help parameter, configured with the {@code help} init parameter, is not required but strongly recommended.
*
Expand All @@ -26,6 +28,8 @@
* <p>The Histogram buckets can be configured with a {@code buckets} init parameter whose value is a comma-separated list
* of valid {@code double} values.
*
* <p>HTTP statuses will be aggregated via Counter. The name for this counter will be derived from the {@code metric-name} init parameter.
*
* <pre>{@code
* <filter>
* <filter-name>prometheusFilter</filter-name>
Expand All @@ -34,7 +38,7 @@
* <param-name>metric-name</param-name>
* <param-value>webapp_metrics_filter</param-value>
* </init-param>
* <init-param>
* <init-param>
* <param-name>help</param-name>
* <param-value>The time taken fulfilling servlet requests</param-value>
* </init-param>
Expand All @@ -56,8 +60,10 @@ public class MetricsFilter implements Filter {
static final String HELP_PARAM = "help";
static final String METRIC_NAME_PARAM = "metric-name";
static final String BUCKET_CONFIG_PARAM = "buckets";
static final String UNKNOWN_HTTP_STATUS_CODE = "";

private Histogram histogram = null;
private Counter statusCounter = null;

// Package-level for testing purposes.
int pathComponents = 1;
Expand Down Expand Up @@ -149,6 +155,10 @@ public void init(FilterConfig filterConfig) throws ServletException {
.help(help)
.name(metricName)
.register();

statusCounter = Counter.build(metricName + "_status_total", "HTTP status codes of " + help)
.labelNames("path", "method", "status")
.register();
}

@Override
Expand All @@ -162,17 +172,28 @@ public void doFilter(ServletRequest servletRequest, ServletResponse servletRespo

String path = request.getRequestURI();

String components = getComponents(path);
String method = request.getMethod();
Histogram.Timer timer = histogram
.labels(getComponents(path), request.getMethod())
.labels(components, method)
.startTimer();

try {
filterChain.doFilter(servletRequest, servletResponse);
} finally {
timer.observeDuration();
statusCounter.labels(components, method, getStatusCode(servletResponse)).inc();
}
}

private String getStatusCode(ServletResponse servletResponse) {
if (!(servletResponse instanceof HttpServletResponse)) {
return UNKNOWN_HTTP_STATUS_CODE;
}

return Integer.toString(((HttpServletResponse) servletResponse).getStatus());
}

@Override
public void destroy() {
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Enumeration;
Expand Down Expand Up @@ -180,4 +181,54 @@ public Void answer(InvocationOnMock invocationOnMock) throws Throwable {
assertEquals(buckets.split(",").length+1, count);
}

@Test
public void testStatusCode() throws Exception {
HttpServletRequest req = mock(HttpServletRequest.class);
when(req.getRequestURI()).thenReturn("/foo/bar/baz/bang");
when(req.getMethod()).thenReturn(HttpMethods.GET);

HttpServletResponse res = mock(HttpServletResponse.class);
when(res.getStatus()).thenReturn(200);

FilterChain c = mock(FilterChain.class);

MetricsFilter constructed = new MetricsFilter(
"foobar_filter",
"Help for my filter",
2,
null
);
constructed.init(mock(FilterConfig.class));

constructed.doFilter(req, res, c);

final Double sampleValue = CollectorRegistry.defaultRegistry.getSampleValue("foobar_filter_status_total", new String[]{"path", "method", "status"}, new String[]{"/foo/bar", HttpMethods.GET, "200"});
assertNotNull(sampleValue);
assertEquals(1, sampleValue, 0.0001);
}

@Test
public void testStatusCodeWithNonHttpServletResponse() throws Exception {
HttpServletRequest req = mock(HttpServletRequest.class);
when(req.getRequestURI()).thenReturn("/foo/bar/baz/bang");
when(req.getMethod()).thenReturn(HttpMethods.GET);

ServletResponse res = mock(ServletResponse.class);

FilterChain c = mock(FilterChain.class);

MetricsFilter constructed = new MetricsFilter(
"foobar_filter",
"Help for my filter",
2,
null
);
constructed.init(mock(FilterConfig.class));

constructed.doFilter(req, res, c);

final Double sampleValue = CollectorRegistry.defaultRegistry.getSampleValue("foobar_filter_status_total", new String[]{"path", "method", "status"}, new String[]{"/foo/bar", HttpMethods.GET, MetricsFilter.UNKNOWN_HTTP_STATUS_CODE});
assertNotNull(sampleValue);
assertEquals(1, sampleValue, 0.0001);
}
}