从Flask迁移至FastAPI时TimedRotatingFileHandler日志报错的解决方法
FastAPI日志配置报错:TimedRotatingFileHandler与Uvicorn Access Logger冲突问题
错误成因
你复用了uvicorn.access这个Logger实例,但Uvicorn为该Logger默认配置了AccessFormatter——这个格式化器专门用于解析HTTP请求的访问日志(格式需包含请求方法、路径、状态码等5个核心字段)。当你直接调用logger.debug("test")传入普通字符串时,不符合AccessFormatter的解析要求,导致格式化时抛出ValueError: not enough values to unpack (expected 5, got 0)。
修复方法
方案1:创建自定义业务日志Logger(推荐)
不要复用Uvicorn的访问日志Logger,独立创建业务Logger,完全自定义日志格式与处理器:
import logging from logging.handlers import TimedRotatingFileHandler from fastapi import FastAPI, Request app = FastAPI() # 创建自定义Logger,避免与Uvicorn日志冲突 logger = logging.getLogger("my_app") logger.setLevel(logging.DEBUG) logger.propagate = False # 防止日志被Uvicorn根Logger重复处理 # 配置日志格式 logformat = logging.Formatter( "[%(asctime)s][%(filename)s:%(lineno)d][%(levelname)s][%(process)d][%(threadName)s] - %(message)s" ) # 添加TimedRotatingFileHandler debug_rotating_handler = TimedRotatingFileHandler( 'pml_debug.log', when="midnight", interval=1, backupCount=15, encoding='utf-8' ) debug_rotating_handler.setFormatter(logformat) debug_rotating_handler.setLevel(logging.DEBUG) logger.addHandler(debug_rotating_handler) @app.get("/") async def home(request: Request): logger.debug("test") return {"test": 1}
方案2:替换Uvicorn Access Logger的Formatter(不推荐)
若坚持复用uvicorn.access,需替换其默认Formatter,但会导致Uvicorn原生的请求访问日志格式被覆盖,丢失请求相关信息:
import logging from logging.handlers import TimedRotatingFileHandler from fastapi import FastAPI, Request app = FastAPI() logformat = logging.Formatter( "[%(asctime)s][%(filename)s:%(lineno)d][%(levelname)s][%(process)d][%(threadName)s] - %(message)s" ) logger = logging.getLogger("uvicorn.access") # 清空原有Handler与Formatter for handler in logger.handlers[:]: logger.removeHandler(handler) # 添加自定义Handler debug_rotating_handler = TimedRotatingFileHandler( 'pml_debug.log', when="midnight", interval=1, backupCount=15, encoding='utf-8' ) debug_rotating_handler.setFormatter(logformat) debug_rotating_handler.setLevel(logging.DEBUG) logger.addHandler(debug_rotating_handler) logger.setLevel(logging.DEBUG) @app.get("/") async def home(request: Request): logger.debug("test") return {"test": 1}
关于异步日志的必要性
- 标准库
TimedRotatingFileHandler是同步IO操作,在FastAPI的异步事件循环中,频繁日志写入会阻塞事件循环,降低并发性能。 - 高QPS、大日志量场景:建议采用异步日志方案,比如用
logging模块的QueueHandler+QueueListener将日志IO转移到独立线程/进程,或使用aiologger等原生异步日志库。 - 低流量场景:同步日志的性能影响可忽略,直接使用
TimedRotatingFileHandler即可。
内容的提问来源于stack exchange,提问作者Gui
相关产品推荐
相关产品推荐

