javaloggingmicronaut

How to get the *pattern* of the current path in Micronaut?


I've got a REST API service up and running using Micronaut 3.10, and am in the process of creating a dashboard to view how much traffic we're getting per endpoint.

Currently we log each request path like this:

@Filter("/**")
@Slf4j
public class LoggingInterceptor implements HttpServerFilter {
  @Override
  public Publisher<MutableHttpResponse<?>> doFilter(HttpRequest<?> request,
                                                    ServerFilterChain chain) {
    log.atInfo().addKeyValue("path", request.getPath());
  }
}

This works great when the path is the same for each request, such as:

GET /api/users

In the logging dashboard, I can just group requests by the above path and it'll match exactly the number of requests hitting that endpoint.

But when the path has a path param in it, such as this:

GET /api/users/{userId}

then grouping by the logged request path is not as useful, since each actual request looks like this:

GET /api/users/UserId123
GET /api/users/UserId234
GET /api/users/UserId345
...

So in my dashboard which groups the logs by request path, it appears that I have hundreds of requests to the path /api/users, but only 1-2 requests to several hundred different paths such as /api/users/UserId123, /api/users/UserId234. Ideally I could just group those all together under /api/users/{userId} instead.

My actual question:

Is there a way to get the path from the request with {userId} instead of UserId123? It would be very useful for this purpose to see what is the wildcard path of the controller that Micronaut resolved the current request to, rather than the actual URL path of the request. Is this possible?


Solution

  • Yes, It is possible,

    Is this what you are looking for?

    @Filter("/**")
    @Slf4j
    public class LoggingInterceptor implements HttpServerFilter {
        @Override
        public Publisher<MutableHttpResponse<?>> doFilter(HttpRequest<?> request,
                                                          ServerFilterChain chain) {
    
            Optional<String> str = request.getAttribute(HttpAttributes.URI_TEMPLATE, String.class);
            if (str.isPresent()) {
                System.out.println(str.get());
            }
    
            return chain.proceed(request);
    
        }
    }
    

    MyController for testing

    @Controller("/api/users")
    public class MyController {
    
        @Get("/{userId}")
        public Integer get(@PathVariable Integer userId) {
    
            return userId;
        }
    }
    

    If you hit the endpoint, the program will print what you expected, which is /api/users/{userId}