关于fastapi repeat_task定时任务修饰器与路由混搭使用日志写入错乱的问题?

我使用fastapi写了一个接口服务,服务内有路由api,也有定时任务,定时任务使用的是@repeat_task修饰器实现的,具体代码可以见

https://github.com/yuval9313/fastapi-restful/blob/master/fastapi_restful/tasks.py

我在使用的过程中遇到了一个问题,日志写入错乱,我需要将日志按照路由的名称\定时任务名称分别写入不同的文件,所以我封装了一个loguru

import sys
from loguru import logger
import os
import datetime
class Logger_task:
    def __init__(self, Log_dir):
        self.logger = logger
        
        # 清空所有设置
        self.logger.remove()
        self.Log_dir=os.path.dirname(os.path.abspath(__file__)) + '\\..\\logs\\{}\\'.format(Log_dir)
        self.logger.add(sys.stdout,
                        format="<green>{time:YYYY-MM-DD HH:mm:ss}</green> | "
                               "<level>{level}</level>: " 
                               "<level>{message}</level>", 
                        )
        file_name = '{}.log'.format(datetime.date.today())
        global_log_file = os.path.join(self.Log_dir, file_name)
        self.logger.add(global_log_file, level='DEBUG',
                        format="<green>{time:YYYY-MM-DD HH:mm:ss}</green> | "
                               "<level>{level}</level>: " 
                               "<level>{message}</level>",  
                        rotation="10 MB")

    def get_task_logger(self):
        return self.logger

定时任务\路由内调用

#定时任务调用
@router.on_event('startup')
@repeat_task(seconds=60, wait_first=True) #一分钟推送一次
def repeat_task_push_FKD():
    from Toollib.logger import Logger
    logger_class = Logger(Log_dir='repeat_task_push_FKD')
    task_logger=logger_class.get_task_logger()
    task_logger.info("推送定时任务-开始")
    push_FKD_class = push_FKD(logger=task_logger)
    push_FKD_class.push_bill()
    task_logger.info("推送定时任务-结束")

#路由api调用
class GetFile(BaseModel):
    file_code: str
    asr_code: str
    file_format: str
    file_name: str  
@router.post("/GetFile")
def GetFile(item: GetFile):
    from Toollib.logger import Logger
    logger_class = Logger(Log_dir='GetFile')
    logger=logger_class.get_logger()
    get_GetFile={"file_code":str(item.file_code),'asr_code':str(item.asr_code),'file_format':str(item.file_format),'file_name':str(item.file_name)}
    logger.info("附件下载-收到获取文件参数:{}".format(str(get_GetFile)))
    Get_File_class = Get_File(file_code=str(item.file_code),asr_code=str(item.asr_code),file_format=str(item.file_format),file_name=str(item.file_name),logger=logger)
    Get_File_bases64 = Get_File_class.Get_bases_64()
    #logger.remove()
    if 'GetResult' in str(Get_File_bases64):
        return respSuccessJson(data=Get_File_bases64['GetResult'], msg='获取成功')
    else:
        return Get_File_bases64

如上调用,当我的repeat_task_push_FKD()定时任务推送的时候,同时对方会调用我的路由/GetFile获取附件。此时定时任务repeat_task_push_FKD()内的后一半的日志会被写入到GetFile的日志文件内,这个是什么问题导致的?我应该怎么修改让它乖乖按照我设定的日志文件写入?
`

阅读 414
avatarAI BotBETA

看起来,你在不同的任务中使用了不同的Logger实例,但没有正确地管理它们。在你的代码中,每次创建一个新的Logger实例时,你都会设置其日志级别、格式和输出位置,但你没有考虑到如果同时运行多个这样的实例,它们可能会互相干扰。

FastAPI框架在处理请求时通常会创建新的上下文,包括日志记录器。如果你在请求处理函数之外创建了日志记录器,那么FastAPI可能无法正确地将其与请求关联起来。

一个可能的解决方案是在每个请求处理函数中创建并使用日志记录器,而不是在整个应用上下文中共享它们。下面是如何做到这一点的一个例子:

from fastapi import FastAPI
from loguru import logger
import os
import datetime

app = FastAPI()

@app.on_event("startup")
async def startup_event():
    logger.add(os.path.dirname(os.path.abspath(__file__)) + "/logs/{}/repeat_task_push_FKD.log")
    logger.add(os.path.dirname(os.path.abspath(__file__)) + "/logs/{}/GetFile.log".format(datetime.date.today()), rotation="10 MB")

@app.post("/GetFile")
async def GetFile(item: GetFile):
    with logger.context():  # 创建一个上下文,这样我们就可以在请求结束时自动清理日志记录器
        logger.info("附件下载-收到获取文件参数:{}".format(str(get_GetFile)))
        Get_File_class = Get_File(file_code=str(item.file_code),asr_code=str(item.asr_code),file_format=str(item.file_format),file_name=str(item.file_name),logger=logger)
        Get_File_bases64 = Get_File_class.Get_bases_64()
        if 'GetResult' in str(Get_File_bases64):
            return respSuccessJson(data=Get_File_bases64['GetResult'], msg='获取成功')
        else:
            return Get_File_bases64

在这个例子中,我使用了FastAPI的@app.on_event("startup")装饰器来初始化日志记录器,并在路由处理函数中使用with logger.context()上下文管理器来记录日志。这样,每个请求都有自己的日志记录器实例,它们不会互相干扰。

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题