Uvicorn 默认不写日志到文件,因启动时重置根日志器并接管handler;basicConfig无效,必须用--log-config指定配置文件,通过dictConfig加载并为uvicorn、uvicorn.access及自定义logger统一添加FileHandler。

Uvicorn 默认不写日志到文件,直接调用 logging.basicConfig(filename=...) 无效 —— 因为 Uvicorn 启动时会重置根日志器,并接管所有 handler。
为什么 basicConfig 在 FastAPI/Uvicorn 中失效
你在 main.py 开头写了:
import logging logging.basicConfig(filename="app.log", level=logging.INFO)
但日志依然只打在终端、文件为空。这不是权限或路径问题,而是 Uvicorn 的启动流程做了三件事:
- 调用
logging.basicConfig()初始化根 logger(覆盖你之前的配置) - 添加一个
StreamHandler到sys.stderr - 忽略你手动加的
FileHandler,因为它没被注册进 Uvicorn 管理的 logger 实例(如"uvicorn"或"uvicorn.access")
必须用 --log-config 指定配置文件
这是最稳定、生产环境唯一推荐的方式。Uvicorn 会用 logging.config.dictConfig() 加载该文件,完全绕过默认初始化逻辑。
创建 logging_config.py:
import logging
from uvicorn.config import LOGGING_CONFIG
<p>LOG_FILE = "app.log"</p><div class="aritcle_card flexRow">
<div class="artcardd flexRow">
<a class="aritcle_card_img" href="/xiazai/gongju/2843" title="Uvicorn 0.52.1"><img
src="https://img.php.cn/upload/manual/001/221/864/6acee78537d78709.png" alt="Uvicorn 0.52.1" ></a>
<div class="aritcle_card_info flexColumn">
<a href="/xiazai/gongju/2843" title="Uvicorn 0.52.1">Uvicorn 0.52.1</a>
<p>Uvicorn 0.52.1 是 Python ASGI Web 服务器,提供异步应用服务及 WebSocket 支持,常用于 FastAPI、Starlette 等应用的开发和部署。本下载为官方 PyPI 通用 wheel 包,可使用 pip 安装,并由包管理器处理所需依赖。</p>
</div>
<a href="/xiazai/gongju/2843" title="Uvicorn 0.52.1" class="aritcle_card_btn flexRow flexcenter"><b></b><span>下载</span> </a>
</div>
</div><h1>修改默认格式(加时间戳)</h1><p>LOGGING_CONFIG["formatters"]["default"]["fmt"] = "%(asctime)s | %(levelprefix)s %(message)s"
LOGGING_CONFIG["formatters"]["access"]["fmt"] = "%(asctime)s | %(levelprefix)s %(client_addr)s - '%(request_line)s' %(status_code)s"</p><h1>添加文件处理器</h1><p>LOGGING_CONFIG["handlers"]["file"] = {
"class": "logging.FileHandler",
"formatter": "default",
"filename": LOG_FILE,
"encoding": "utf-8",
"mode": "a",
}</p><h1>将其挂载到 uvicorn 和 uvicorn.access 日志器</h1><p>LOGGING_CONFIG["loggers"]["uvicorn"]["handlers"].append("file")
LOGGING_CONFIG["loggers"]["uvicorn.access"]["handlers"].append("file")</p>启动命令必须显式指定:
uvicorn main:app --log-config logging_config.py
- 不要漏掉
--log-config参数,否则配置不生效 - 文件路径是相对于当前工作目录的,不是相对于
main.py - 如果用
uvicorn.run()编程式启动,需传入log_config="logging_config.py"
别碰 uvicorn.run() 里的 log_level 或 access_log
这些参数只能开关/调整级别,不能新增输出目标。比如:
uvicorn.run("main:app", log_level="debug", access_log=False)它只会让终端日志变多或变少,不会让任何内容写进文件。常见误操作包括:
- 以为设了
log_level="info"就能触发文件写入 —— 不会 - 把
access_log=False当成“关闭终端日志”,其实只是关掉访问日志,错误和启动日志还在终端刷 - 在
uvicorn.run()里传logger=xxx,但没同步改LOGGING_CONFIG结构 —— handler 仍不生效
自定义应用日志也要走同一套配置
你在路由函数里用 logging.getLogger("myapp") 打日志,想让它也进 app.log?可以,但得手动把 "file" handler 加到那个 logger:
logger = logging.getLogger("myapp")
logger.addHandler(logging.getLogger("uvicorn").handlers[-1]) # 复用已配好的 FileHandler
logger.setLevel(logging.INFO)
更稳妥的做法是:在 logging_config.py 里一并配置 "myapp" logger,并确保它继承 "uvicorn" 的 handlers。否则你会看到一部分日志进文件、一部分只在终端——这种割裂是生产排查中最难定位的。
真正容易被忽略的点是:Uvicorn 的 "uvicorn" 和 "uvicorn.access" 是两个独立 logger,它们的 handlers 必须分别追加;而你的业务 logger 如果没显式关联,就永远游离在文件输出之外。

















