Search code examples
pythonloggingfastapiuvicorn

Python Logging with loguru- log request params on Fastapi app


I have a fastapi application and I want to log every request made on it. I'm trying to use loguru and uvicorn for this, but I don't know how to print the headers and request params (if have one) associated with each request.

I want something like this:

INFO 2020-08-13 13:36:33.494 uvicorn.protocols.http.h11_impl:send - 127.0.0.1:52660 - "GET 
/url1/url2/ HTTP/1.1" 400 params={"some": value, "some1":value}

Is there a way ? thanks for your help.

Here some links:

loguru uvicorn fastapi


Solution

  • A dependency on a router level could be used (thanks to @lsabi in the comments below):

    import sys
    
    import uvicorn
    
    from fastapi import FastAPI, Request, APIRouter, Depends
    from loguru import logger
    from starlette.routing import Match
    
    logger.remove()
    logger.add(sys.stdout, colorize=True, format="<green>{time:HH:mm:ss}</green> | {level} | <level>{message}</level>")
    app = FastAPI()
    
    router = APIRouter()
    
    
    async def logging_dependency(request: Request):
        logger.debug(f"{request.method} {request.url}")
        logger.debug("Params:")
        for name, value in request.path_params.items():
            logger.debug(f"\t{name}: {value}")
        logger.debug("Headers:")
        for name, value in request.headers.items():
            logger.debug(f"\t{name}: {value}")
    
    
    @router.get("/{param1}/{param2}")
    async def path_operation(param1: str, param2: str):
        return {'param1': param1, 'param2': param2}
    
    app.include_router(router, dependencies=[Depends(logging_dependency)])
    
    if __name__ == "__main__":
        uvicorn.run("app:app", host="localhost", port=8001)
    

    More sophisticated approach is using a middleware for logging every request and doing matching manually:

    import sys
    
    import uvicorn
    
    from fastapi import FastAPI, Request
    from loguru import logger
    from starlette.routing import Match
    
    logger.remove()
    logger.add(sys.stdout, colorize=True, format="<green>{time:HH:mm:ss}</green> | {level} | <level>{message}</level>")
    app = FastAPI()
    
    
    @app.middleware("http")
    async def log_middle(request: Request, call_next):
        logger.debug(f"{request.method} {request.url}")
        routes = request.app.router.routes
        logger.debug("Params:")
        for route in routes:
            match, scope = route.matches(request)
            if match == Match.FULL:
                for name, value in scope["path_params"].items():
                    logger.debug(f"\t{name}: {value}")
        logger.debug("Headers:")
        for name, value in request.headers.items():
            logger.debug(f"\t{name}: {value}")
    
        response = await call_next(request)
        return response
    
    
    @app.get("/{param1}/{param2}")
    async def path_operation(param1: str, param2: str):
        return {'param1': param1, 'param2': param2}
    
    
    if __name__ == "__main__":
        uvicorn.run("app:app", host="localhost", port=8001)
    

    curl http://localhost:8001/admin/home

    Output:

    16:06:43 | DEBUG | GET http://localhost:8001/admin/home
    16:06:43 | DEBUG | Params:
    16:06:43 | DEBUG |  param1: admin
    16:06:43 | DEBUG |  param2: home
    16:06:43 | DEBUG | Headers:
    16:06:43 | DEBUG |  host: localhost:8001
    16:06:43 | DEBUG |  user-agent: curl/7.64.0
    16:06:43 | DEBUG |  accept: */*