2 minute read

Hello 🙋‍♂️,

Running a ⏩FastAPI ⏩ application in production is very easy and fast, but along the way some Uvicorn logs are lost.

In this article I will discuss how to write a custom UvicornWorker and to centralize your logging configuration into a single file.

To keep things as simple as possible I’ve put all my code in a single Python file.

main.py

import uvicorn as uvicornfrom fastapi import FastAPI, APIRouterrouter = APIRouter(prefix="")def create_app():    fast_app = FastAPI()    fast_app.include_router(router)    return fast_app@router.get("/")def read_root():    return {"Hello": "World"}if __name__ == '__main__':    app = create_app()    uvicorn.run(app=app)

Running the code will return a {"Hello": "World"} json when you visit the root endpoint / at http://127.0.0.1:8000. 😁

When you check the console window, the following log lines are printed:

INFO:     Started server process [10276]INFO:     Waiting for application startup.INFO:     Application startup complete.INFO:     Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)INFO:     127.0.0.1:53491 - "GET / HTTP/1.1" 200 OK

Notice the Uvicorn log GET / HTTP/1.1″ 200 OK.

According to Uvicorn’s deployment docs we should run Uvicorn in a production settings with the following command: gunicorn -k uvicorn.workers.UvicornWorker main:create_app.

(venv2) ➜  FastAPILogging gunicorn -k uvicorn.workers.UvicornWorker main:create_app[2021-05-17 22:10:44 +0300] [6250] [INFO] Starting gunicorn 20.1.0[2021-05-17 22:10:44 +0300] [6250] [INFO] Listening at: http://127.0.0.1:8000 (6250)[2021-05-17 22:10:44 +0300] [6250] [INFO] Using worker: uvicorn.workers.UvicornWorker[2021-05-17 22:10:44 +0300] [6252] [INFO] Booting worker with pid: 6252[2021-05-17 22:10:45 +0300] [6252] [WARNING] ASGI app factory detected. Using it, but please consider setting the --factory flag explicitly.[2021-05-17 22:10:45 +0300] [6252] [INFO] Started server process [6252][2021-05-17 22:10:45 +0300] [6252] [INFO] Waiting for application startup.[2021-05-17 22:10:45 +0300] [6252] [INFO] Application startup complete.

Now, if we visit the root endpoint, the console won’t print “GET / HTTP/1.1” 200 OK anymore/ 🤦‍♂️.

To fix it we need a custom UvicornWorker ⚙ and a logging configuration file 🗃.

Create a new file and name it logging.yaml, then paste the following contents in it:

version: 1disable_existing_loggers: falseformatters:  standard:    format: "%(asctime)s - %(levelname)s - %(message)s"handlers:  console:    class: logging.StreamHandler    formatter: standard    stream: ext://sys.stdoutloggers:  uvicorn:    error:      propagate: trueroot:  level: INFO  handlers: [console]  propagate: no

This file will configure our root logger and our Uvicorn logger. To read more on the topic please see Python logging configuration.

Next, we will create a custom UvicornWorker class that will set log_config to the path of our logging.yaml file, to pass the logging configuration that we’ve just made to Uvicorn. 🦄

I added the following code in main.py:

class MyUvicornWorker(UvicornWorker):    CONFIG_KWARGS = {        "log_config": "/mnt/c/Users/denis/PycharmProjects/FastAPILogging/logging.yaml",    }

▶ If we run the application with:

gunicorn -k main.MyUvicornWorker main:create_app

We should see the Uvicorn access logs printed in the console 🦄

(venv2) ➜  FastAPILogging gunicorn -k main.MyUvicornWorker main:create_app[2021-05-17 22:31:28 +0300] [6278] [INFO] Starting gunicorn 20.1.0[2021-05-17 22:31:28 +0300] [6278] [INFO] Listening at: http://127.0.0.1:8000 (6278)[2021-05-17 22:31:28 +0300] [6278] [INFO] Using worker: main.MyUvicornWorker[2021-05-17 22:31:28 +0300] [6280] [INFO] Booting worker with pid: 62802021-05-17 22:31:28,185 - WARNING - ASGI app factory detected. Using it, but please consider setting the --factory flag explicitly.2021-05-17 22:31:28,185 - INFO - Started server process [6280]2021-05-17 22:31:28,185 - INFO - Waiting for application startup.2021-05-17 22:31:28,185 - INFO - Application startup complete.2021-05-17 22:31:30,129 - INFO - 127.0.0.1:54004 - "GET / HTTP/1.1" 200

Thanks for reading! 📚

requirements.txt

click==7.1.2fastapi==0.65.1gunicorn==20.1.0h11==0.12.0httptools==0.2.0pydantic==1.8.2PyYAML==5.4.1starlette==0.14.2typing-extensions==3.10.0.0uvicorn==0.13.4uvloop==0.15.2