1.Script logging log

This method is the simplest and most straightforward, how to record logs when writing scripts, how to record logs here, usually is to first configure the log format, and then logger.info where needed:

Configure logging.

import logging

logger = logging.getLogger()
logger.setLevel(logging.INFO)
ch = logging.StreamHandler()
fh = logging.FileHandler(filename='./server.log')
formatter = logging.Formatter(
    "%(asctime)s - %(module)s - %(funcName)s - line:%(lineno)d - %(levelname)s - %(message)s"
)

ch.setFormatter(formatter)
fh.setFormatter(formatter)
logger.addHandler(ch) #Exporting logs to the screen
logger.addHandler(fh) #Exporting logs to a file

If you are afraid that the file will be too large, you can use the circular log.

fh = logging.handlers.RotatingFileHandler("api.log",mode="a",maxBytes = 100*1024, backupCount = 3)

Then, add logger.info / logger.warning / logger.debug / logger.error to the places where you need to log

from fastapi import FastAPI

# setup loggers
import logging
logger = logging.getLogger()
logger.setLevel(logging.INFO)
ch = logging.StreamHandler()
fh = logging.FileHandler(filename='./server.log'))
ch.setFormatter(LogFormatter())
fh.setFormatter(LogFormatter())
logger.addHandler(ch) #Exporting logs to the screen
logger.addHandler(fh) #Exporting logs to a file



app = FastAPI()

@app.get("/")
async def root():
    logger.info("logging from the root logger")
    return {"status""alive"}

You may say, I have a lot of interfaces, should I add them one by one?

No, you can intercept all requests in the middleware and log each request, the complete code is shown below.

File name main.py, focus on log_requests function.

import logging
from fastapi import FastAPI
import time
import random
import string

logger = logging.getLogger()
logger.setLevel(logging.INFO)
ch = logging.StreamHandler()
fh = logging.FileHandler(filename='./server.log')
formatter = logging.Formatter(
    "%(asctime)s - %(module)s - %(funcName)s - line:%(lineno)d - %(levelname)s - %(message)s"
)

ch.setFormatter(formatter)
fh.setFormatter(formatter)
logger.addHandler(ch) #Exporting logs to the screen
logger.addHandler(fh) #Exporting logs to a file


logger = logging.getLogger(__name__)

app = FastAPI()

@app.middleware("http")
async def log_requests(request, call_next):
    idem = ''.join(random.choices(string.ascii_uppercase + string.digits, k=6))
    logger.info(f"rid={idem} start request path={request.url.path}")
    start_time = time.time()
    
    response = await call_next(request)
    
    process_time = (time.time() - start_time) * 1000
    formatted_process_time = '{0:.2f}'.format(process_time)
    logger.info(f"rid={idem} completed_in={formatted_process_time}ms status_code={response.status_code}")
    
    return response


@app.get("/")
async def root():
    return {"status""alive"}

Command line uvicorn main:app --host 0.0.0.0 --port 8081 Then visit http://localhost:8081 and you will see the log output, which will also be saved in the server.log file as follows.

2.Record uvicorn's logs

fastapi is actually uvicorn-driven, and uvicorn itself outputs the following message in the terminal.

❯ uvicorn main:app --host 0.0.0.0 --port 8081
INFO:     Started server process [88064]
INFO:     Waiting for application startup.
INFO:     Application startup complete.
INFO:     Uvicorn running on http://0.0.0.0:8081 (Press CTRL+C to quit)

Logging this information to a file is sufficient, and can be configured when fastapi is started at

@app.on_event("startup")
async def startup_event():
    logger = logging.getLogger("uvicorn.access")
    handler = logging.handlers.RotatingFileHandler("api.log",mode="a",maxBytes = 100*1024, backupCount = 3)
    handler.setFormatter(logging.Formatter("%(asctime)s - %(levelname)s - %(message)s"))
    logger.addHandler(handler)

This way, the output of uvicorn is logged in api.log.

3.Configuring uvicorn's logs

If you are running FastApi in this way.

app = FastAPI()
uvicorn.run(app, host="0.0.0.0", port=8000)

Then you can configure uvicorn's logging in code, and then pass in the logging configuration information in the run function, and you're good to go: the

log_config = uvicorn.config.LOGGING_CONFIG
log_config["formatters"]["access"]["fmt"] = "%(asctime)s - %(levelname)s - %(message)s"
log_config["formatters"]["default"]["fmt"] = "%(asctime)s - %(levelname)s - %(message)s"
uvicorn.run(app, log_config=log_config)

Of course, the configuration file can also be passed in from the command line via uvicorn --log-config=log.ymal.

version: 1
formatters:
  simple:
    format: '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
handlers:
  console:
    class: logging.StreamHandler
    level: DEBUG
    formatter: simple
    stream: ext://sys.stdout
loggers:
  simpleExample:
    level: DEBUG
    handlers: [console]
    propagate: no
root:
  level: DEBUG
  handlers: [console]

Log files support .ini, .json, .yaml formats.

keywords: python