question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Include routing information for middleware

See original GitHub issue

Is there a way to obtain which route the request is taking in middleware? Right now, the only available thing is request.url.path, which doesn’t give me a nice name for metrics.

I see in https://github.com/steinnes/timing-asgi/blob/master/timing_asgi/integrations/starlette.py that it basically just loops through all the routes to obtain the matching one. Is there a better way to do it?

Looping in that manner also runs into an issue where if there are multiple matching routes, like /resource/default/1 /resource/1/1 it can’t find the right one since they both match.

Issue Analytics

  • State:open
  • Created 4 years ago
  • Reactions:11
  • Comments:22 (6 by maintainers)

github_iconTop GitHub Comments

3reactions
allan-simoncommented, Nov 28, 2019

sure thing

"""The goal is to have the route name of the current request                                                                                                                                 
accessible anywhere by our loggers to help us contextualizing                                                                                                                                
when debugging though kibana or other centralized log visualizer                                                                                                                             
"""                                                                                                                                                                                          
from typing import Callable                                                                                                                                                                  
from typing import Optional                                                                                                                                                                  
from contextvars import ContextVar                                                                                                                                                           
                                                                                                                                                                                             
from fastapi import APIRouter, FastAPI                                                                                                                                                       
from fastapi.routing import APIRoute                                                                                                                                                         
from starlette.requests import Request                                                                                                                                                       
from starlette.responses import Response                                                                                                                                                     
                                                                                                                                                                                             
                                                                                                                                                                                             
_route_name_ctx_var: ContextVar[Optional[str]] = ContextVar("route_name", default=None)                                                                                                      
                                                                                                                                                                                             
                                                                                                                                                                                             
def get_route_name() -> Optional[str]:                                                                                                                                                       
    return _route_name_ctx_var.get()                                                                                                                                                         
                                                                                                                                                                                             
                                                                                                                                                                                             
class LoggedRoute(APIRoute):                                                                                                                                                                 
    def get_route_handler(self) -> Callable:                                                                                                                                                 
        original_route_handler = super().get_route_handler()                                                                                                                                 
        route_name = self.name                                                                                                                                                               
                                                                                                                                                                                             
        # we import here to avoid the circular dependencies                                                                                                                                  
        from app.logging import get_logger                                                                                                                                                   
                                                                                                                                                                                             
        async def custom_route_handler(request: Request) -> Response:                                                                                                                        
                                                                                                                                                                                             
            _route_name_ctx_var.set(route_name)                                                                                                                                              
                                                                                                                                                                                             
            app_logger = get_logger()                                                                                                                                                        
            app_logger.info({"step": "start"})                                                                                                                                               
                                                                                                                                                                                             
            response: Response = await original_route_handler(request)                                                                                                                       
                                                                                                                                                                                             
            app_logger.info({"step": "end", "status_code": response.status_code})                                                                                                            
                                                                                                                                                                                             
            return response                                                                                                                                                                  
                                                                                                                                                                                             
        return custom_route_handler              

and then I use it like this:

from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy.engine import Connection

from app.database import get_connection
from app.logged_route import LoggedRoute


router = APIRouter(route_class=LoggedRoute)

1reaction
basepicommented, Sep 21, 2022

@adriangb It does make sense, but doesn’t really work well for APM agents (Which is the use case for @billcrook and I).

We’re trying to name our APM traces using the route name instead of the URL, since the latter has cardinality problems. (And routes are a better way to group traces than URL anyway)

We want minimal work on the part of the user – add our middleware, and we do all the work for you, creating traces for each transaction that comes through your app.

Currently this require us to iterate over the routes to get the route name, as I mentioned before. It has minor performance implications, besides being a bit hacky.

I’ll understand if this is a situation where our use case isn’t one you want to support, but unfortunately requiring the user to add a middleware to each route is probably a worse solution than the iterating we’re doing.

I can’t speak to whether your solution would work for @cypai.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Using middleware - Express.js
Express is a routing and middleware web framework that has minimal functionality of its own: An Express application is essentially a series of...
Read more >
Routing Middleware - Slim Framework
The routing has been implemented as middleware. We are still using FastRoute as the default router but it is not tightly coupled to...
Read more >
Accessing route values in endpoint middleware in ASP.NET ...
In this post I describe how you can access the route values from middleware when using endpoint routing in ASP.NET Core 3.0.
Read more >
Middleware - Laravel - The PHP Framework For Web Artisans
Sometimes you may want to group several middleware under a single key to make them easier to assign to routes. You may accomplish...
Read more >
Getting route data in your ASP.NET Core middleware - RIMdev
UseEndpointRouting () // Registering this prior to your middleware unlocks the ✨. ... Add(nameof(Post), post); } } await next(context); } } ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found