FASTAPI logging to file

Viewed 16994

Why doesnt FastAPI create the log file when I am logging

Here is the code that I used

from fastapi.logger import logger as fastapi_logger
from logging.handlers import RotatingFileHandler
import logging

formatter = logging.Formatter(
        "[%(asctime)s.%(msecs)03d] %(levelname)s [%(thread)d] - %(message)s", "%Y-%m-%d %H:%M:%S")
handler = RotatingFileHandler('logfile.log', backupCount=0)
logging.getLogger("fastapi")
fastapi_logger.addHandler(handler)
handler.setFormatter(formatter)

fastapi_logger.info('****************** Starting Server *****************')

the file 'logfie.log' isn't generated automatically unlike in flask. is there any other configuration for this?

5 Answers

You'll need to run FastAPI via some server, like Uvicorn, Hypercorn, or AsyncIO

Even though logging might be configured correctly in your FastAPI files, you are running the app via the server. You do not need to configure handlers at FastAPI level. Just use logger.info, your logs will be sent to server.

To configure Uvicorn logging you'll need to use pass config to Uvicorn as dict config or file config as mentioned in https://docs.python.org/3/library/logging.config.html#logging.config.dictConfig and https://www.uvicorn.org/settings/#logging

Here is a sample log.ini file

[loggers]
keys=root

[handlers]
keys=logfile

[formatters]
keys=logfileformatter

[logger_root]
level=INFO
handlers=logfile

[formatter_logfileformatter]
format=[%(asctime)s.%(msecs)03d] %(levelname)s [%(thread)d] - %(message)s

[handler_logfile]
class=handlers.RotatingFileHandler
level=INFO
args=('logfile.log','a')
formatter=logfileformatter

Then use it as uvicorn example:app --log-config /path/to/log.ini

Hypercorn and AsyncIO have their own way of passing the config object https://docs.python.org/3/library/asyncio-dev.html

I wanted to know how to get FastAPI to log to both a file and the console. Pratik's answer got me most of the way there, but I found that Uvicorn was only logging to the file. At least, this was the case when Uvicorn was integrated into my main script like so:

if __name__ == "__main__":
    cwd = pathlib.Path(__file__).parent.resolve()
    uvicorn.run(app, host="0.0.0.0", port=5000, log_config=f"{cwd}/log.ini")

So I thought I would share my adapted solution for anybody who may encounter the same issue when attempting to log to both the console and a file.

[loggers]
keys=root

[handlers]
keys=logfile,logconsole

[formatters]
keys=logformatter

[logger_root]
level=INFO
handlers=logfile, logconsole

[formatter_logformatter]
format=[%(asctime)s.%(msecs)03d] %(levelname)s [%(thread)d] - %(message)s

[handler_logfile]
class=handlers.RotatingFileHandler
level=INFO
args=('logfile.log','a')
formatter=logformatter

[handler_logconsole]
class=handlers.logging.StreamHandler
level=INFO
args=()
formatter=logformatter

I think problem is that you use fastapi logger, but there are also uvicorn.error, uvicorn.access and uvicorn loggers, try to add handler to this loggers

I do not know about using the fastAPI logger, but using a logging from Python seems similar and can auto-generate the log file you name. For example:

import logging

def get_logger(name, level=logging.DEBUG) -> logging.Logger:
    FORMAT = "[%(levelname)s  %(name)s %(module)s:%(lineno)s - %(funcName)s() - %(asctime)s]\n\t %(message)s \n"
    TIME_FORMAT = "%d.%m.%Y %I:%M:%S %p"

    FILENAME = 'example/path/log.log'

    logging.basicConfig(format=FORMAT, datefmt=TIME_FORMAT, level=level,
    filename=FILENAME
    )

    logger = logging.getLogger(name)
    return logger

#in any file that import fn get_logger, you can set up local logger like:
local_logger = get_logger(__name__)

local_logger.info(f'I am a local logger. I am writing to file {FILENAME}. If that file did not exist, it would be automatically created.')

You can try this code, I am on Python 3.9.5 :

import logging

# create log file
logging.basicConfig(filename='mylog.log', encoding='utf-8', level=logging.DEBUG)

# add worning info and error
logging.debug('go to log file')
logging.info('for informations')
logging.warning('and this for worning')
logging.error('for error')
Related