Configuration of FastAPI logging - locally and in production

FastAPI is a fantastic Web framework for the rapid construction of APIs with Python. But sometimes logging can be a challenge, especially if you want to make sure it works properly both locally with Uvicorn and in production. In this tutorial, I'll show you how to configure FastAPI logging so that it works reliably both locally and in production environments.

Preparation

First we need a standard FastAPI application. Create a file called api.py and insert the following code:

Copy pythonCodefrom fastapi import FastAPI
import logging
import uvicorn
app = FastAPI(title="api")
LOG = logging.getLogger(__name__)
LOG.info("API is starting up")
LOG.info(uvicorn.Config.asgi_version)
@app.get("/")
async def get_index():
    LOG.info("GET /")
    return {"Hello": "Api"}

Problem definition

When running the application locally with Uvicorn, the logging is not displayed as expected:

Copy bashCodeuvicorn api:app --reload

This leads to the following information in the console:

Copy textCodeINFO: Will watch for changes in these directories: ['/Users/user/code/backend/api']
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
INFO: Started reloader process [44258] using StatReload
INFO: Started server process [44260]
INFO: Waiting for application startup.
INFO: Application startup complete.

But the startup messages that we have defined in our code are missing.

Solution

To configure logging correctly, we need to make sure that we add both a StreamHandler and a FileHandler and set the log levels accordingly.

Step-by-step instructions

1. configure logging

Create or modify your api.py as follows:

Copy pythonCodefrom fastapi import FastAPI
import logging
import sys
app = FastAPI(title='api')
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)
# StreamHandler for the console
stream_handler = logging.StreamHandler(sys.stdout)
log_formatter = logging.Formatter("%(asctime)s [%(processName)s: %(process)d] [%(threadName)s: %(thread)d] [%(levelname)s] %(name)s: %(message)s")
stream_handler.setFormatter(log_formatter)
logger.addHandler(stream_handler)
logger.info('API is starting up')
@app.get('/')
async def main():
    logger.info('GET /')
    return 'ok'

2. use the Uvicorn logger

We can also use the Uvicorn logger directly to ensure that the log messages are displayed correctly:

Copy pythonCodeimport logging
from fastapi import FastAPI
app = FastAPI(title='api')
logger = logging.getLogger('uvicorn.error')
@app.get('/')
async def main():
    logger.debug('this is a debug message')
    return 'ok'

3. set log level

The log level is set by the Uvicorn command line option --log-level debug controlled:

Copy bashCodeuvicorn api:app --reload --log-level debug

This will lead to the following expenses:

Copy textCodeINFO: Uvicorn running on http://0.0.0.0:8000 (Press CTRL+C to quit)
DEBUG: this is a debug message

4. production environment

In the production environment, you can use Uvicorn with gunicorn to obtain a more robust solution:

Copy bashCodegunicorn -k uvicorn.workers.UvicornWorker api:app --log-level debug

Conclusion

By adding and configuring StreamHandlers and FileHandlers and setting the log level correctly, you can ensure that your FastAPI application works both locally and in the Production is logged properly. This makes debugging and monitoring your application much easier.

Good luck with the implementation and happy coding!

"
"
Florian Zyprian Avatar

Latest articles