How to log operation id and status for each route? #6577
-
First Check
Commit to Help
Example Code@app.middleware("http")
async def log(request, call_next):
response = call_next(request)
if response.status_code >= 300:
return response
operation_id = get_operation_id_somehow() # is this possible?
print("LOG", operation_id, get_access_token(request))
return responseDescriptionI want to log the Operation ID, as well as the access token (from the request) for all requests that did not error, is this at all possible right now? Given that I want to do this for all endpoints, using a middleware seems reasonable, but they have no way of accessing the route information (including the operation id). I can get the other bits of information from the requests/response no problem. I also considered using a dependency, but even dependencies seem unable to have access to the operation id. Operating SystemmacOS Operating System DetailsNo response FastAPI Version0.65.2 Python VersionPython 3.9.1 Additional ContextNo response |
Beta Was this translation helpful? Give feedback.
Replies: 8 comments
-
Something like the below would work: |
Beta Was this translation helpful? Give feedback.
-
|
@dstlny thank you for the suggestion. I don't think that would work on variadic paths, would it? Also, I'm using sub-apps/sub-routers so would have to further change it to understand that. It also feels wrong to do route resolution twice (fastapi already does it once to run the actual request), is there really no better way than manually resolve the route again? |
Beta Was this translation helpful? Give feedback.
-
I think it should work in most cases. I was under the impression that at some point FastAPI/Starlette included the route in which the path matched in the request object, but I appear to be wrong. I don't think there's much we can do here but resolve it twice to do what you wanna do. You might be able to pre-build this in a data-structure, say
after you've mounted all your sub-routers/sub-apps, so all you'd need to do is a simple |
Beta Was this translation helpful? Give feedback.
-
|
I can't read everything, so you can ignore if it's not useful, but: https://github.com/tomwojcik/starlette-context |
Beta Was this translation helpful? Give feedback.
-
|
Okay, this works but it does feel silly to do: @app.middleware("http")
async def print_op_id(request, call_next):
response = await call_next(request)
print(request.method, request.url.path, op_id(app.routes, request.scope))
return response
def op_id(routes, scope):
for route in routes:
if route.matches(scope):
if isinstance(route, APIRoute):
return route.operation_id
elif isinstance(route, Mount):
return op_id(route.routes, scope) |
Beta Was this translation helpful? Give feedback.
-
from fastapi import Request
from fastapi.routing import APIRoute
class Route(APIRoute):
"""Extension Route and log request.json()"""
def get_route_handler(self):
original_route_handler = super().get_route_handler()
async def log_request_some(request: Request):
logger.info('some some some')
return await original_route_handler(request)
return log_request_detail
router = APIRouter(route_class=Route) |
Beta Was this translation helpful? Give feedback.
-
|
I'm currently working on the same situation... this seems to work so far: """Instrumentation for registering requests and responses."""
import logging
from typing import Callable
from fastapi import Request, Response
from fastapi.routing import APIRoute
logger = logging.getLogger(__name__)
class LoggingRoute(APIRoute): # noqa
def get_route_handler(self) -> Callable: # noqa
original_route_handler = super().get_route_handler()
async def custom_route_handler(request: Request) -> Response: # noqa
logger.warning("====REQUEST SCOPE====")
logger.warning(request.scope)
logger.warning("=====================")
logger.warning("====REQUEST BODY JSON====")
body_json = await request.json()
logger.warning(body_json)
logger.warning("=====================")
response = await original_route_handler(request)
logger.warning("====RESPONSE JSON====")
logger.warning(response.body)
logger.warning("=====================")
return response
return custom_route_handlerHowever, you need to inject this class into the router before adding routes to it because the (internal?) An example on how to do this: router = APIRouter(prefix="/foo", route_class=LoggingRoute)
@router.post(...)
def post_method(...)
# etc
|
Beta Was this translation helpful? Give feedback.
-
|
I've ended up using the solution suggested in #3877 (comment) |
Beta Was this translation helpful? Give feedback.
I'm currently working on the same situation... this seems to work so far: