diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/TraceWebFilterAutoConfiguration.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/TraceWebFilterAutoConfiguration.java index d8937132b8..fa812f3b6a 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/TraceWebFilterAutoConfiguration.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/TraceWebFilterAutoConfiguration.java @@ -23,6 +23,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.actuate.trace.TraceRepository; import org.springframework.boot.actuate.trace.WebRequestTraceFilter; +import org.springframework.boot.actuate.web.BasicErrorController; import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; @@ -42,6 +43,9 @@ public class TraceWebFilterAutoConfiguration { @Autowired private TraceRepository traceRepository; + @Autowired(required = false) + private BasicErrorController errorController; + @Value("${management.dump_requests:false}") private boolean dumpRequests; @@ -49,6 +53,9 @@ public class TraceWebFilterAutoConfiguration { public WebRequestTraceFilter webRequestLoggingFilter(BeanFactory beanFactory) { WebRequestTraceFilter filter = new WebRequestTraceFilter(this.traceRepository); filter.setDumpRequests(this.dumpRequests); + if (this.errorController != null) { + filter.setErrorController(this.errorController); + } return filter; } diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/trace/WebRequestTraceFilter.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/trace/WebRequestTraceFilter.java index cffdc5d6ac..8ddb0b5afa 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/trace/WebRequestTraceFilter.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/trace/WebRequestTraceFilter.java @@ -34,6 +34,7 @@ import javax.servlet.http.HttpServletResponse; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.springframework.boot.actuate.web.BasicErrorController; import org.springframework.core.Ordered; import com.fasterxml.jackson.core.JsonProcessingException; @@ -56,6 +57,8 @@ public class WebRequestTraceFilter implements Filter, Ordered { private ObjectMapper objectMapper = new ObjectMapper(); + private BasicErrorController errorController; + /** * @param traceRepository */ @@ -122,6 +125,7 @@ public class WebRequestTraceFilter implements Filter, Ordered { String value = response.getHeader(header); headers.put(header, value); } + headers.put("status", "" + response.getStatus()); @SuppressWarnings("unchecked") Map allHeaders = (Map) trace.get("headers"); allHeaders.put("response", headers); @@ -151,6 +155,13 @@ public class WebRequestTraceFilter implements Filter, Ordered { trace.put("method", request.getMethod()); trace.put("path", request.getRequestURI()); trace.put("headers", allHeaders); + Throwable error = (Throwable) request + .getAttribute("javax.servlet.error.exception"); + if (error != null) { + if (this.errorController != null) { + trace.put("error", this.errorController.error(request)); + } + } return trace; } @@ -162,4 +173,8 @@ public class WebRequestTraceFilter implements Filter, Ordered { public void destroy() { } + public void setErrorController(BasicErrorController errorController) { + this.errorController = errorController; + } + } diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/trace/WebRequestTraceFilterTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/trace/WebRequestTraceFilterTests.java index ec97771d6d..69bd26b01c 100644 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/trace/WebRequestTraceFilterTests.java +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/trace/WebRequestTraceFilterTests.java @@ -19,6 +19,7 @@ package org.springframework.boot.actuate.trace; import java.util.Map; import org.junit.Test; +import org.springframework.boot.actuate.web.BasicErrorController; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletResponse; @@ -55,6 +56,38 @@ public class WebRequestTraceFilterTests { this.filter.enhanceTrace(trace, response); @SuppressWarnings("unchecked") Map map = (Map) trace.get("headers"); - assertEquals("{Content-Type=application/json}", map.get("response").toString()); + assertEquals("{Content-Type=application/json, status=200}", map.get("response") + .toString()); + } + + @Test + public void filterHasResponseStatus() { + MockHttpServletRequest request = new MockHttpServletRequest("GET", "/foo"); + MockHttpServletResponse response = new MockHttpServletResponse(); + response.setStatus(404); + response.addHeader("Content-Type", "application/json"); + Map trace = this.filter.getTrace(request); + this.filter.enhanceTrace(trace, response); + @SuppressWarnings("unchecked") + Map map = (Map) ((Map) trace + .get("headers")).get("response"); + assertEquals("404", map.get("status").toString()); + } + + @Test + public void filterHasError() { + this.filter.setErrorController(new BasicErrorController()); + MockHttpServletRequest request = new MockHttpServletRequest("GET", "/foo"); + MockHttpServletResponse response = new MockHttpServletResponse(); + response.setStatus(500); + request.setAttribute("javax.servlet.error.exception", new IllegalStateException( + "Foo")); + response.addHeader("Content-Type", "application/json"); + Map trace = this.filter.getTrace(request); + this.filter.enhanceTrace(trace, response); + @SuppressWarnings("unchecked") + Map map = (Map) trace.get("error"); + System.err.println(map); + assertEquals("Foo", map.get("message").toString()); } } diff --git a/spring-boot-samples/spring-boot-sample-actuator/src/test/java/org/springframework/boot/sample/ops/SampleActuatorApplicationTests.java b/spring-boot-samples/spring-boot-sample-actuator/src/test/java/org/springframework/boot/sample/ops/SampleActuatorApplicationTests.java index aed6d030f8..66213efee0 100644 --- a/spring-boot-samples/spring-boot-sample-actuator/src/test/java/org/springframework/boot/sample/ops/SampleActuatorApplicationTests.java +++ b/spring-boot-samples/spring-boot-sample-actuator/src/test/java/org/springframework/boot/sample/ops/SampleActuatorApplicationTests.java @@ -16,6 +16,10 @@ package org.springframework.boot.sample.ops; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + import java.io.IOException; import java.util.ArrayList; import java.util.List; @@ -43,10 +47,6 @@ import org.springframework.security.crypto.codec.Base64; import org.springframework.web.client.DefaultResponseErrorHandler; import org.springframework.web.client.RestTemplate; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - /** * Basic integration tests for service demo application. * @@ -134,13 +134,27 @@ public class SampleActuatorApplicationTests { @Test public void testErrorPage() throws Exception { + ResponseEntity entity = getRestTemplate().getForEntity( + "http://localhost:8080/health", String.class); + assertEquals(HttpStatus.OK, entity.getStatusCode()); + assertEquals("ok", entity.getBody()); + } + + @Test + public void testTrace() throws Exception { + getRestTemplate().getForEntity( + "http://localhost:8080/health", String.class); @SuppressWarnings("rawtypes") - ResponseEntity entity = getRestTemplate("user", getPassword()).getForEntity( - "http://localhost:8080/foo", Map.class); - assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, entity.getStatusCode()); + ResponseEntity entity = getRestTemplate("user", getPassword()).getForEntity( + "http://localhost:8080/trace", List.class); + assertEquals(HttpStatus.OK, entity.getStatusCode()); @SuppressWarnings("unchecked") - Map body = entity.getBody(); - assertEquals(500, body.get("status")); + List> list = (List>) entity.getBody(); + Map trace = list.get(list.size()-1); + @SuppressWarnings("unchecked") + Map map = (Map) ((Map) ((Map) + trace.get("info")).get("headers")).get("response"); + assertEquals("200", map.get("status")); } @Test