Announcement Announcement Module
Collapse
No announcement yet.
How to log every uncaught exception regardless of other handlers Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • How to log every uncaught exception regardless of other handlers

    Okay it took me almost an hour to figure this out, and it's the 3rd project I've had to figure it out for, so I'm posting it here in the hopes that nobody else experiences such pain for something that should take 30 seconds to configure. I'll be very happy to admit that I'm simply "doing it wrong", but egad it shouldn't be so hard to figure out. Skip to the bottom if just want to see the solution.

    Spring's documentation is very hand-wavy on the subject--there are myriad ways to handle specific exceptions, but apparently none out of the box to log ALL uncaught exceptions with Spring MVC at the level of your choosing even if they are in fact resolved by other handlers as well. This is dev 101, I want the message and stack trace in my log, always, period.

    Some examples online have you sub-classing DefaultHandlerExceptionResolver and setting the warnLogCategory property. That's great, except:
    1. It means you are stuck with "warn" level.
    2. If another resolver handles the exception first, it will never get called.
    3. With the MVC configuration in my current project, I couldn't even override DefaultHandlerExceptionResolver. I could register the bean, sure, or a custom sub-class, but introspection demonstrated that there were actually two at runtime, the one I registered and the one DispatcherServlet registers by default. How do you tell Spring to use yours instead? Just set the order property? If so then you still have two, just only yours will ever get invoked.

    K 'nuff said, this is incredibly simple to do. Here's the exception resolver:

    Code:
    package my.package;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.web.servlet.ModelAndView;
    import org.springframework.web.servlet.handler.AbstractHandlerExceptionResolver;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    public class LoggingExceptionResolver extends AbstractHandlerExceptionResolver {
        private static final Logger logger = LoggerFactory.getLogger(LoggingExceptionResolver.class);
    
        @Override
        protected ModelAndView doResolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
            logger.warn("uncaught exception in controllers/handlers", ex);
            return null;
        }
    }
    Configure it in your web application context xml and you're done:

    Code:
      
    <bean class="my.package.LoggingExceptionResolver">
          <property name="order" value="-1"/>
    </bean>
    Setting the order to -1 should make sure it always gets called. (dusts off hands and walks away)

    Note: This is not a perfect solution. The behavior of AbstractHandlerExceptionResolver's logger means the message part of the exception will get logged as debug, then LoggingExceptionResolver gets called and logs the message and trace at warn. That's because AbstractHandlerExceptionResolver sets the log category to getClass() (i.e. the sub-class) instead of AbstractHandlerExceptionResolver.class. Personally I consider this an anti-pattern because then it's difficult to tell where in the code the message is actually coming from. It seems to be popular in the Spring codebase though. Probably you could change the implementation to not sub-class AbstractHandlerExceptionResolver but this is good enough for me.
Working...
X