I want to add logging to my FastAPI application. I use the standard logging module in python for this. Logging is needed to collect application data while it is running. Just logging HTTP requests is not enough.
Simple FastAPI app for example:
import uvicorn
from fastapi import FastAPI
import logging
from logging.handlers import RotatingFileHandler, SysLogHandler
from logging import StreamHandler
import logging.config
class LogClass:
def __init__(self,
log_format: str = f"%(asctime)s - [%(levelname)s] - (%(filename)s).%(funcName)s(%(lineno)d) - %(message)s",
log_name: str = None,
maxFileSize: int = 1000000,
backupCount: int = 3,
syslog_host: str = None,
syslog_port: int = 514):
self.log_backupCount = backupCount
self.log_maxFileSize = maxFileSize
self.log_format = log_format
self.syslog_host = syslog_host
self.syslog_port = syslog_port
self.log_name = log_name
self.logger = self.get_logger()
def get_file_handler(self):
file_handler = RotatingFileHandler(self.log_name, maxBytes=self.log_maxFileSize,
backupCount=self.log_backupCount)
file_handler.setLevel(logging.INFO)
file_handler.setFormatter(logging.Formatter(self.log_format))
return file_handler
def get_stream_handler(self):
stream_handler = StreamHandler()
stream_handler.setLevel(logging.INFO)
stream_handler.setFormatter(logging.Formatter(self.log_format))
return stream_handler
def get_syslog_handler(self):
syslog_handler = SysLogHandler(address=(self.syslog_host, self.syslog_port))
syslog_handler.setLevel(logging.INFO)
syslog_handler.setFormatter(logging.Formatter(self.log_format))
return syslog_handler
def get_logger(self, stream: bool = True) -> logging.Logger:
logger = logging.getLogger()
logger.setLevel(logging.INFO)
if self.log_name is not None:
logger.addHandler(self.get_file_handler())
if self.syslog_host is not None and self.syslog_port is not None:
logger.addHandler(self.get_syslog_handler())
if stream:
logger.addHandler(self.get_stream_handler())
return logger
app = FastAPI()
@app.get("/start")
async def start_func():
log = LogClass()
logger = log.logger
logger.info("Message to log")
return {"result": "OK"}
if __name__ == "__main__":
uvicorn.run("test2_app:app", host="127.0.0.1", port=8000, reload=True)
PYTHONUNBUFFERED var set to 1.
But when I make simple curl requests, I see duplicate messages in the log. Something like this, when I send only three requests:
INFO: 127.0.0.1:52144 - "GET /start HTTP/1.1" 200 OK
2023-06-08 10:29:40,947 - [INFO] - (test2_app.py).start_func(70) - Message to log
2023-06-08 10:29:49,083 - [INFO] - (test2_app.py).start_func(70) - Message to log
2023-06-08 10:29:49,083 - [INFO] - (test2_app.py).start_func(70) - Message to log
INFO: 127.0.0.1:53004 - "GET /start HTTP/1.1" 200 OK
INFO: 127.0.0.1:53012 - "GET /start HTTP/1.1" 200 OK
2023-06-08 10:29:51,986 - [INFO] - (test2_app.py).start_func(70) - Message to log
2023-06-08 10:29:51,986 - [INFO] - (test2_app.py).start_func(70) - Message to log
2023-06-08 10:29:51,986 - [INFO] - (test2_app.py).start_func(70) - Message to log
I don't understand where the messages are duplicated. Can anyone help me get the correct log?