Reputation: 71961
I have Spring MVC setup to log exceptions using commons logging, but find that some runtime exceptions aren't being logged.
Here's my bean configuration for the default exception resolver provided by spring:
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<property name="exceptionMappings">
<props>
<prop key="java.lang.Exception">error</prop>
</props>
</property>
</bean>
Upvotes: 6
Views: 10462
Reputation: 49085
To augment what @Brad Parks said I recommend that you also make a HttpServlet Filter that will run first (and finish last) to catch exceptions that Spring will miss. Also because there is Spring code that can fail like Spring Security and custom Http Interceptors that fall outside of the SimpleMappingException Resolver (ie if the DispatchServlet or Interceptors fail you will not get the exception).
Here's an example filter I use on top of the SimpleMappingExceptionResolver.
public class ExceptionLoggerServletFilter implements Filter {
private CommonAccessLogFormatter requestFormat = new CommonAccessLogFormatter();
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException,
ServletException {
try {
chain.doFilter(request, response);
} catch (Exception e) {
StringBuilder sb = new StringBuilder("Very Bad shit has happened:");
if (request instanceof HttpServletRequest)
requestFormat.appendLogEntry((HttpServletRequest) request, sb);
log.fatal(sb.toString(), e);
throw new ServletException(e);
}
}
@Override
public void destroy() {
}
private static final Logger log = Logger.getLogger(ExceptionLoggerServletFilter.class);
}
The reason I recommend this is that for most servlet containers uncaught exception may or may not be logged with probably not your logging framework (for Tomcat its JULI).
The commons log access formatter just logs the request similar to Apache log files.
Upvotes: 3
Reputation: 357
In my test, use SimpleMappingExceptionResolver
can't log MissingServletRequestParameterException
, I combined @ControllerAdvice
and Filter
to do log.
Use ControllerAdvice
to catch Throwable raised in Spring MVC Controller.
@ControllerAdvice
public class GlobalDefaultExceptionHandler {
private static final Logger logger = LoggerFactory.getLogger("global_controller_exception_logger");
@ExceptionHandler(value = Throwable.class)
public void defaultErrorHandler(Throwable e) throws Throwable {
// If the exception is annotated with @ResponseStatus rethrow it and let
// the framework handle it.
// AnnotationUtils is a Spring Framework utility class.
if (AnnotationUtils.findAnnotation
(e.getClass(), ResponseStatus.class) != null) {
throw e;
}
// Otherwise log exception
logger.error("global controller default exception handler", e);
throw e;
}
@ExceptionHandler(MissingServletRequestParameterException.class)
public void httpBadRequest(Exception e, HttpServletRequest request) throws Exception {
StringBuffer requestURL = request.getRequestURL();
logger.warn("{} HTTP Status 400 - {}", requestURL, e.getMessage());
throw e;
}
}
Use Filter
to catch additional Exception.
@WebFilter(
filterName = "ExceptionLogFilter",
urlPatterns = "/*",
dispatcherTypes = {DispatcherType.REQUEST, DispatcherType.ASYNC, DispatcherType.ERROR}
)
public class ExceptionLogFilter implements Filter {
private static final Logger logger = LoggerFactory.getLogger("global_filter_exception_logger");
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
try {
chain.doFilter(request, response);
} catch (IOException | ServletException e) {
logger.error("bad thing happened during doFilter", e);
throw e;
}
}
......
}
logback configuration
<logger name="global_controller_exception_logger" level="info"/>
<logger name="global_filter_exception_logger" level="info"/>
You can view my gist for full code.
Upvotes: 2
Reputation: 71961
To get this to log most exceptions, I had to add the following line to my config:
<property name="warnLogCategory" value="someCategoryStringYouMakeUp" />
So ultimately it became the following:
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<property name="warnLogCategory" value="apperror" />
<property name="exceptionMappings">
<props>
<prop key="java.lang.Exception">error</prop>
</props>
</property>
</bean>
warnLogCategory is described in detail here.
Upvotes: 4