Make sure the MetricsFilter uses committed response's status

Previously, if an exception was thrown during request handling after
the response had been committed, i.e. after the status and headers
had been written, the metrics filter would assume that it was a 500
response. This was potentially inaccurate as the status had already
been sent to the client and before the exception was thrown and it
may have been something other than a 500.

This commit updates MetricsFilter so that it will use the
status from the response if the response has been committed even when
an exception is thrown.

Closes gh-7277
This commit is contained in:
Andy Wilkinson 2016-11-03 20:08:31 +00:00
parent 6a87df8e46
commit 97e5e32496
2 changed files with 34 additions and 0 deletions

View File

@ -109,6 +109,9 @@ final class MetricsFilter extends OncePerRequestFilter {
}
finally {
if (!request.isAsyncStarted()) {
if (response.isCommitted()) {
status = getStatus(response);
}
stopWatch.stop();
request.removeAttribute(ATTRIBUTE_STOP_WATCH);
recordMetrics(request, path, status, stopWatch.getTotalTimeMillis());

View File

@ -365,6 +365,37 @@ public class MetricFilterAutoConfigurationTests {
context.close();
}
@Test
public void whenExceptionIsThrownResponseStatusIsUsedWhenResponseHasBeenCommitted()
throws Exception {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.register(Config.class, MetricFilterAutoConfiguration.class);
context.refresh();
Filter filter = context.getBean(Filter.class);
final MockHttpServletRequest request = new MockHttpServletRequest("GET",
"/test/path");
final MockHttpServletResponse response = new MockHttpServletResponse();
FilterChain chain = mock(FilterChain.class);
willAnswer(new Answer<Object>() {
@Override
public Object answer(InvocationOnMock invocation) throws Throwable {
response.setStatus(200);
response.setCommitted(true);
throw new IOException();
}
}).given(chain).doFilter(request, response);
try {
filter.doFilter(request, response, chain);
fail();
}
catch (IOException ex) {
// Continue
}
verify(context.getBean(CounterService.class))
.increment(eq("status.200.test.path"));
context.close();
}
@Configuration
public static class Config {