Skip to content

Commit 9210029

Browse files
committed
Record trace with response status of 500 following unhandled exception
Previously, if the filter chain threw an unhandled exception, WebRequestTraceFilter would record a trace with a response status of 200. This occurred because response.getStatus() would return 200 as the container had not yet caught the exception and mapped it to an error response. This commit updates WebRequestTraceFilter to align its behaviour with MetricsFilter. It now assumes that the response status will be a 500 and only updates that to the status of the response if the call to the filter chain returns successfully. To avoid making a breaking change to the signature of the protected enhanceTrace method, an HttpServletResponseWrapper is used to include the correct status in the trace. Closes gh-5331
1 parent 2e54078 commit 9210029

File tree

2 files changed

+55
-1
lines changed

2 files changed

+55
-1
lines changed

spring-boot-actuator/src/main/java/org/springframework/boot/actuate/trace/WebRequestTraceFilter.java

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import javax.servlet.ServletException;
3030
import javax.servlet.http.HttpServletRequest;
3131
import javax.servlet.http.HttpServletResponse;
32+
import javax.servlet.http.HttpServletResponseWrapper;
3233
import javax.servlet.http.HttpSession;
3334

3435
import org.apache.commons.logging.Log;
@@ -37,6 +38,7 @@
3738
import org.springframework.boot.actuate.trace.TraceProperties.Include;
3839
import org.springframework.boot.autoconfigure.web.ErrorAttributes;
3940
import org.springframework.core.Ordered;
41+
import org.springframework.http.HttpStatus;
4042
import org.springframework.web.context.request.ServletRequestAttributes;
4143
import org.springframework.web.filter.OncePerRequestFilter;
4244

@@ -108,11 +110,14 @@ protected void doFilterInternal(HttpServletRequest request,
108110
throws ServletException, IOException {
109111
Map<String, Object> trace = getTrace(request);
110112
logTrace(request, trace);
113+
int status = HttpStatus.INTERNAL_SERVER_ERROR.value();
111114
try {
112115
filterChain.doFilter(request, response);
116+
status = response.getStatus();
113117
}
114118
finally {
115-
enhanceTrace(trace, response);
119+
enhanceTrace(trace, status == response.getStatus() ? response
120+
: new CustomStatusResponseWrapper(response, status));
116121
this.repository.add(trace);
117122
}
118123
}
@@ -214,4 +219,21 @@ public void setErrorAttributes(ErrorAttributes errorAttributes) {
214219
this.errorAttributes = errorAttributes;
215220
}
216221

222+
private static final class CustomStatusResponseWrapper
223+
extends HttpServletResponseWrapper {
224+
225+
private final int status;
226+
227+
private CustomStatusResponseWrapper(HttpServletResponse response, int status) {
228+
super(response);
229+
this.status = status;
230+
}
231+
232+
@Override
233+
public int getStatus() {
234+
return this.status;
235+
}
236+
237+
}
238+
217239
}

spring-boot-actuator/src/test/java/org/springframework/boot/actuate/trace/WebRequestTraceFilterTests.java

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,12 @@
3737
import org.springframework.mock.web.MockHttpServletRequest;
3838
import org.springframework.mock.web.MockHttpServletResponse;
3939

40+
import static org.hamcrest.Matchers.equalTo;
41+
import static org.hamcrest.Matchers.is;
4042
import static org.junit.Assert.assertEquals;
43+
import static org.junit.Assert.assertThat;
4144
import static org.junit.Assert.assertTrue;
45+
import static org.junit.Assert.fail;
4246
import static org.mockito.Mockito.spy;
4347
import static org.mockito.Mockito.times;
4448
import static org.mockito.Mockito.verify;
@@ -183,4 +187,32 @@ public void filterHasError() {
183187
assertEquals("Foo", map.get("message").toString());
184188
}
185189

190+
@Test
191+
@SuppressWarnings("unchecked")
192+
public void filterHas500ResponseStatusWhenExceptionIsThrown()
193+
throws ServletException, IOException {
194+
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/foo");
195+
MockHttpServletResponse response = new MockHttpServletResponse();
196+
197+
try {
198+
this.filter.doFilterInternal(request, response, new FilterChain() {
199+
200+
@Override
201+
public void doFilter(ServletRequest request, ServletResponse response)
202+
throws IOException, ServletException {
203+
throw new RuntimeException();
204+
}
205+
206+
});
207+
fail("Exception was swallowed");
208+
}
209+
catch (RuntimeException ex) {
210+
Map<String, Object> headers = (Map<String, Object>) this.repository.findAll()
211+
.iterator().next().getInfo().get("headers");
212+
Map<String, Object> responseHeaders = (Map<String, Object>) headers
213+
.get("response");
214+
assertThat((String) responseHeaders.get("status"), is(equalTo("500")));
215+
}
216+
}
217+
186218
}

0 commit comments

Comments
 (0)