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:
from 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:
uvicorn api:app --reload
This leads to the following information in the console:
INFO: 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:
from fastapi import FastAPI
import logging
import sys
app = FastAPI(title='api')
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)
# StreamHandler für die Konsole
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:
import 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:
uvicorn api:app --reload --log-level debug
This will lead to the following expenses:
INFO: 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:
gunicorn -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!
