Recently, when I was using FastAPI, I found that the official documentation of FastAPI did not have instructions related to configuring logs. Today, I will share three methods of configuring logs for FastAPI.
The first one, logging like writing a script
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 in the required place a:
Configuration Log:
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 might say, "I have a particularly large number of interfaces, should I add them line by line?
No, you can intercept all requests inside the middleware and log each request, the full 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 is also saved in the server.log file:
The second one, logging uvicorn logs
fastapi is actually uvicorn-driven, and uvicorn itself will output information 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)
Just record this information in a file, which can be configured when fastapi starts:
@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)
这样,uvicorn 的输出,就会记录在 api.log 中。
Third, configure uvicorn's logs
If you are running FastApi this way:
app = FastAPI()
uvicorn.run(app, host="0.0.0.0", port=8000)
Then you can configure uvicorn's logging in the code, and then pass in the logging configuration information in the run function, and you're done:
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.