Uvicorn启动报ERROR: Exception in ASGI application主因是应用首次响应时崩溃,关键线索在堆栈顶端(如TypeError),常见于app变量被覆盖、全局对象未初始化、Depends异常或lifespan startup失败;需curl复现并定位traceback中首个自定义模块路径。

Uvicorn 启动时直接报 ERROR: Exception in ASGI application
这通常不是日志配置问题,而是应用本身在首次响应请求时就崩溃了。错误堆栈最顶端那行(比如 TypeError: 'QApplication' object is not callable)才是关键线索。
常见诱因:
-
app变量被意外覆盖(如混用 PyQt5 的QApplication()赋值给同名变量) - 路由函数里调用了未初始化的全局对象(如数据库连接、模型加载器)
- 依赖注入函数(
Depends)抛出异常,且没加 try/catch - FastAPI 的
lifespan事件中startup阶段失败,但 Uvicorn 默认不阻断启动(只打 INFO 级日志),导致后续请求一来就崩
实操建议:先在终端手动 curl 一次根路径,复现错误;然后盯住 traceback 中第一个 File ".../applications.py", line ... 或你自己的模块路径,那里就是真出问题的地方。
Uvicorn 日志里看不到 502 或请求体内容
Uvicorn 默认只记录连接建立、响应状态码和耗时,不记录原始请求头/体、也不记录上游关闭连接这类“非本进程主动返回”的错误——比如 Nginx 报 502 时,Uvicorn 根本不知道发生了什么。
如果你需要排查这类问题:
- 加
--log-level debug只能多看到 ASGI 生命周期事件,对 502 无帮助 - 真正要查 502,得看反向代理(Nginx / Traefik / Cloud Load Balancer)的日志,关键词是
upstream prematurely closed connection - 想捕获原始请求数据?得自己写中间件,用
request.scope["method"]和await request.body()提取,注意别在生产环境无条件记录,会吃光磁盘 - Uvicorn 的
access-log默认关着,启动时加--access-log才输出 access log,但它也不含请求体
后台运行时 ERROR: [Errno 98] Address already in use 不停复现
这不是日志输出问题,是进程没杀干净。nohup 启动后,ps aux | grep uvicorn 看到的 PID 往往只是 shell wrapper,真正的 worker 进程可能以 python 名义躲在后台,且处于 TIME_WAIT 状态。
可靠清理方式(Linux/macOS):
Uvicorn 0.52.1 是 Python ASGI Web 服务器,提供异步应用服务及 WebSocket 支持,常用于 FastAPI、Starlette 等应用的开发和部署。本下载为官方 PyPI 通用 wheel 包,可使用 pip 安装,并由包管理器处理所需依赖。
- 查所有监听 8000 的进程:
sudo lsof -t -i tcp:8000(返回多个 PID 就全杀) - 强制终止整个进程组:
kill -9 $(lsof -t -i tcp:8000) - 如果用 systemd 管理,别手杀,改用
systemctl restart your-uvicorn-service - Windows 下用 PowerShell:
Get-NetTCPConnection -LocalPort 8000 | ForEach-Object { Stop-Process -Id $_.OwningProcess -Force }
换端口只是绕开症状,不是解决根本——TIME_WAIT 是 TCP 协议行为,等 2–4 分钟自动消失,但开发期频繁重启时,建议统一加 --host 127.0.0.1,避免绑定到 0.0.0.0 引发的跨网络栈冲突(尤其 WSL2/Docker Desktop 环境)。
想让 Uvicorn 记录 Invalid HTTP request 这类底层协议错误
Uvicorn 底层用 httptools 解析 HTTP,当它收到畸形请求(比如爬虫乱发、代理截断、TLS 握手失败后硬塞 HTTP 包),默认只打一条警告,不带原始数据,很难定位来源。
临时调试可改源码(不推荐上生产):
找到 httptools_impl.py 中类似 self.parser.feed_data(data) 的位置,在 HttpParserError 分支里加日志,例如:
except httptools.HttpParserError as exc:
self.logger.warning("Invalid HTTP request received. Raw data: %r", data[:128])
self.transport.close()
更稳妥的做法是前置加一层 Nginx,用 log_subrequest on + 自定义 log_format 记录所有进来的原始字节流,Uvicorn 层专注业务逻辑即可。
真正难处理的从来不是日志“怎么输出”,而是日志里那些没出现的内容——比如静默丢弃的请求、没触发的异常分支、被中间件吞掉的错误。盯住第一条 ERROR,顺藤摸到代码里那个 app 变量,比调半天日志格式有用得多。










