pythonloggingfastapi

How to configure FastAPI logging so that it works both with Uvicorn locally and in production?


I have an api.py file with the following:

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"}

The application locally is run with:

uvicorn api:app --reload
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.

It is not logging any of the startup messages.

Later on when sending a http request to the api:

INFO:     127.0.0.1:50538 - "POST /api/v1/endpoint HTTP/1.1" 200 OK

In the function body there is LOG.info("example") that does not get logged either.

Is there a way to make FastAPI logging work with Uvicorn and also in production (independently of the execution environments like Uvicorn).


Solution

  • Make sure to add a StreamHandler and/or FileHandler and set the level/severity (i.e., DEBUG, INFO, WARNING, etc). The following example is based on this answer and this answer. More details and examples can be found in Python's official documentation page here. You may also want to have a look at all the available LogRecord attributes that can be used to format the logging records.

    Working Example

    from fastapi import FastAPI
    import logging
    import sys
    
    
    app = FastAPI(title='api')
    
    logger = logging.getLogger(__name__)
    logger.setLevel(logging.DEBUG)
    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'