3

I want to create a filter that would log the time spent for particular web request along with the operation name. However, some URLs have path parameters and I'm looking for something that would allow me to get a URL pattern that corresponds to the particular method instead of URL itself.

For example, for method that handles GET requests for URLs like /users/{id}/accounts/{type} I want to see operation name like GET /users/{}/accounts/{} instead of, say: GET /users/1/accounts/facebook or GET /users/2/accounts/twitter?someParam=3.

Right now I solve this by matching URL with predefined patterns in the tool that parses log records but to me it looks like a hack.

I didn’t find an easy way to access URL pattern associated with a method that handles incoming request, so one possible solution that I’m considering right now is setting a thread local variable in the controller method itself and access it in my HTTP filter but this also doesn’t seem like a clean way to solve this.

Is there any better solution to this problem?

To set up some context: I'm using Spring MVC 4.

Alex
  • 2,916
  • 3
  • 22
  • 27
  • 1
    If you have your filter extend GenericFilterBean it can be Spring manged. You can then inject an instance of the configured RequestMappingHandlerMapping which is what you would seem to need to find the mapping for a given request. I have not been able to work out how to proceed from here but see http://stackoverflow.com/questions/14025872/reflectively-getting-list-of-spring-mvc-controllers-matching-specific-url which may give you some ideas as to how you would get the matching url pattern. – Alan Hay Nov 07 '16 at 21:24
  • 1
    OK, looks like I see how to make it working. I'll update my question if I decide to stick with this approach with the working code - unless I decide to stick to something else. – Alex Nov 11 '16 at 20:22
  • @AlanHay I posted an answer, it turned out to be much simpler than I hoped it to be. – Alex Nov 18 '16 at 01:20

1 Answers1

2

OK, it turned out to be super-simple. With spring, you can do it as follows:

public class HttpOperationLoggerFilter extends OncePerRequestFilter {
  private final Logger log = LoggerFactory.getLogger(getClass());

  @Override
  protected void doFilterInternal(HttpServletRequest request,
                                  HttpServletResponse response,
                                  FilterChain filterChain) throws ServletException, IOException {
      // do filter first
      filterChain.doFilter(request, response);

      // ... then you'll be able to access spring MVC's bestMatchingPattern!
      final Object urlPattern = request
        .getAttribute("org.springframework.web.servlet.HandlerMapping.bestMatchingPattern");
      log.info("urlPattern={}", urlPattern);
  }
}

So, if you have MVC controller's method that handles URL patterns like '/do/{something}/like/{that}' you'll see exactly that pattern!

Good thing is that this variable contains composite pattern, so if your controller is annotated with RequestMapping, its value will be prepended to the pattern.

Alex
  • 2,916
  • 3
  • 22
  • 27