From 5656e83ba9516b3345cea999d716660417a6001b Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Tue, 24 May 2016 13:34:22 +0200 Subject: [PATCH] Enable logging of resolved exceptions Previously, if an exception was resolved by a `HandlerExceptionResolver` nothing the log indicated a failure to process the query. This commit adds a new property `spring.mvc.log-resolved-exception` that enables warning logs for supported `HandlerExceptionResolver` instances. When Devtools is enabled, this flag is enabled by default. Closes gh-2176 --- .../web/WebMvcAutoConfiguration.java | 21 +++++++++++ .../autoconfigure/web/WebMvcProperties.java | 13 +++++++ .../web/WebMvcAutoConfigurationTests.java | 36 +++++++++++++++++++ ...DevToolsPropertyDefaultsPostProcessor.java | 3 +- .../appendix-application-properties.adoc | 1 + 5 files changed, 73 insertions(+), 1 deletion(-) diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/WebMvcAutoConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/WebMvcAutoConfiguration.java index 7fbf20ab4f..5b1766728a 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/WebMvcAutoConfiguration.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/WebMvcAutoConfiguration.java @@ -68,6 +68,7 @@ import org.springframework.web.filter.HiddenHttpMethodFilter; import org.springframework.web.filter.HttpPutFormContentFilter; import org.springframework.web.filter.RequestContextFilter; import org.springframework.web.servlet.DispatcherServlet; +import org.springframework.web.servlet.HandlerExceptionResolver; import org.springframework.web.servlet.LocaleResolver; import org.springframework.web.servlet.View; import org.springframework.web.servlet.ViewResolver; @@ -81,6 +82,7 @@ import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry import org.springframework.web.servlet.config.annotation.ViewControllerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport; import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; +import org.springframework.web.servlet.handler.AbstractHandlerExceptionResolver; import org.springframework.web.servlet.handler.SimpleUrlHandlerMapping; import org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver; import org.springframework.web.servlet.i18n.FixedLocaleResolver; @@ -415,6 +417,25 @@ public class WebMvcAutoConfiguration { } return super.createExceptionHandlerExceptionResolver(); } + + @Override + protected void configureHandlerExceptionResolvers( + List exceptionResolvers) { + super.configureHandlerExceptionResolvers(exceptionResolvers); + if (exceptionResolvers.isEmpty()) { + addDefaultHandlerExceptionResolvers(exceptionResolvers); + } + if (this.mvcProperties.isLogResolvedException()) { + for (HandlerExceptionResolver resolver : exceptionResolvers) { + if (resolver instanceof AbstractHandlerExceptionResolver) { + ((AbstractHandlerExceptionResolver) resolver) + .setWarnLogCategory(resolver.getClass() + .getName()); + } + } + } + } + } @Configuration diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/WebMvcProperties.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/WebMvcProperties.java index 0817991039..c9a288022f 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/WebMvcProperties.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/WebMvcProperties.java @@ -78,6 +78,11 @@ public class WebMvcProperties { */ private boolean throwExceptionIfNoHandlerFound = false; + /** + * Enable warn logging of exceptions resolved by a "HandlerExceptionResolver". + */ + private boolean logResolvedException = false; + /** * Maps file extensions to media types for content negotiation, e.g. yml->text/yaml. */ @@ -144,6 +149,14 @@ public class WebMvcProperties { this.throwExceptionIfNoHandlerFound = throwExceptionIfNoHandlerFound; } + public boolean isLogResolvedException() { + return this.logResolvedException; + } + + public void setLogResolvedException(boolean logResolvedException) { + this.logResolvedException = logResolvedException; + } + public Map getMediaTypes() { return this.mediaTypes; } diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/WebMvcAutoConfigurationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/WebMvcAutoConfigurationTests.java index d3e791f8b2..c1abfaba1e 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/WebMvcAutoConfigurationTests.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/WebMvcAutoConfigurationTests.java @@ -28,12 +28,14 @@ import java.util.Map; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import org.assertj.core.api.Condition; import org.joda.time.DateTime; import org.junit.After; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; +import org.springframework.beans.DirectFieldAccessor; import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration; import org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration.WebMvcAutoConfigurationAdapter; @@ -58,15 +60,18 @@ import org.springframework.web.accept.ContentNegotiationManager; import org.springframework.web.bind.support.ConfigurableWebBindingInitializer; import org.springframework.web.filter.HttpPutFormContentFilter; import org.springframework.web.servlet.HandlerAdapter; +import org.springframework.web.servlet.HandlerExceptionResolver; import org.springframework.web.servlet.HandlerMapping; import org.springframework.web.servlet.LocaleResolver; import org.springframework.web.servlet.View; import org.springframework.web.servlet.ViewResolver; import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; +import org.springframework.web.servlet.handler.HandlerExceptionResolverComposite; import org.springframework.web.servlet.handler.SimpleUrlHandlerMapping; import org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver; import org.springframework.web.servlet.i18n.FixedLocaleResolver; +import org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter; import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter; import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; import org.springframework.web.servlet.resource.AppCacheManifestTransformer; @@ -523,6 +528,37 @@ public class WebMvcAutoConfigurationTests { .isNotInstanceOf(MyRequestMappingHandlerAdapter.class); } + @Test + public void defaultLogResolvedException() { + load(); + testLogResolvedExceptionCustomization(false); + } + + @Test + public void customLogResolvedException() { + load("spring.mvc.log-resolved-exception:true"); + testLogResolvedExceptionCustomization(true); + } + + private void testLogResolvedExceptionCustomization(final boolean expected) { + HandlerExceptionResolver exceptionResolver = this.context.getBean( + HandlerExceptionResolver.class); + assertThat(exceptionResolver).isInstanceOf(HandlerExceptionResolverComposite.class); + List delegates = + ((HandlerExceptionResolverComposite) exceptionResolver).getExceptionResolvers(); + for (HandlerExceptionResolver delegate : delegates) { + if (delegate instanceof AbstractHandlerMethodAdapter) { + assertThat(new DirectFieldAccessor(delegate).getPropertyValue("warnLogger")) + .is(new Condition() { + @Override + public boolean matches(Object value) { + return (expected ? value != null : value == null); + } + }); + } + } + } + private void load(Class config, String... environment) { this.context = new AnnotationConfigEmbeddedWebApplicationContext(); EnvironmentTestUtils.addEnvironment(this.context, environment); diff --git a/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/env/DevToolsPropertyDefaultsPostProcessor.java b/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/env/DevToolsPropertyDefaultsPostProcessor.java index 5683aa65ba..c3814a03fe 100755 --- a/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/env/DevToolsPropertyDefaultsPostProcessor.java +++ b/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/env/DevToolsPropertyDefaultsPostProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2015 the original author or authors. + * Copyright 2012-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -52,6 +52,7 @@ public class DevToolsPropertyDefaultsPostProcessor implements EnvironmentPostPro properties.put("spring.h2.console.enabled", "true"); properties.put("spring.resources.cache-period", "0"); properties.put("spring.template.provider.cache", "false"); + properties.put("spring.mvc.log-resolved-exception", "true"); PROPERTIES = Collections.unmodifiableMap(properties); } diff --git a/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc b/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc index d2940df769..5bfa9e4af8 100644 --- a/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc +++ b/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc @@ -338,6 +338,7 @@ content into your application; rather pick only the properties that you need. spring.mvc.ignore-default-model-on-redirect=true # If the content of the "default" model should be ignored during redirect scenarios. spring.mvc.locale= # Locale to use. By default, this locale is overridden by the "Accept-Language" header. spring.mvc.locale-resolver=accept-header # Define how the locale should be resolved. + spring.mvc.log-resolved-exception=false # Enable warn logging of exceptions resolved by a "HandlerExceptionResolver". spring.mvc.media-types.*= # Maps file extensions to media types for content negotiation. spring.mvc.message-codes-resolver-format= # Formatting strategy for message codes. For instance `PREFIX_ERROR_CODE`. spring.mvc.servlet.load-on-startup=-1 # Load on startup priority of the Spring Web Services servlet.