Resolving Uvicorn Worker Access Log Formatting Issues in FastAPI
Deploying a FastAPI application using Gunicorn with Uvicorn workers typically involves a command such as:
gunicorn app_core:web_app --workers 2 --worker-class uvicorn.workers.UvicornWorker --bind 127.0.0.1:8000 --access-logfile '-'
By default, the generated access logs lack timestamps, outputting only basic request details:
176.123.7.11:0 - "GET / HTTP/1.0" 200
Attempting to append request times using Gunicorn's native --access-logformat argument or a gunicorn.conf.py configuration file has no effect. This happens because the Uvicorn worker class overrides Gunicorn's access log formatting mechanisms, rendering standard Gunicorn logging configurations ineffective.
A previously suggested workaround required patching the Gunicorn source code (glogging.py), but a cleaner approach exists by configuring the uvicorn.access logger directly within FastAPI.
First, remove the --access-logfile '-' parameter from the Gunicorn startup command. Then, inject a custom logging handler during the FastAPI application lifespan setup.
import logging
from contextlib import asynccontextmanager
from fastapi import FastAPI
CUSTOM_LOG_PATTERN = "%(asctime)s - %(levelname)s - %(message)s"
def setup_custom_access_logger():
access_logger = logging.getLogger("uvicorn.access")
console_handler = logging.StreamHandler()
console_handler.setFormatter(logging.Formatter(CUSTOM_LOG_PATTERN))
access_logger.addHandler(console_handler)
@asynccontextmanager
async def application_startup(web_application):
setup_custom_access_logger()
yield
web_app = FastAPI(lifespan=application_startup)
After applying this configuration, the access logs will correctly output timestamps alongside the request details:
2024-02-25 16:02:48,435 - INFO - 127.0.0.1:52872 - "GET /docs HTTP/1.1" 418
2024-02-25 16:09:56,785 - INFO - 127.0.0.1:46822 - "POST /close_pid?pid=-1 HTTP/1.1" 200
Note: This lifespan approach requires FastAPI version 0.100 or later.