yappi在FastAPI+Uvicorn多线程环境下无法捕获非主线程数据
FastAPI+Uvicorn下yappi无法捕获业务线程调用的解决方案
核心原因
Uvicorn默认采用多进程模式启动服务,主线程中启动的yappi追踪不会被fork出的子进程继承,而业务代码实际运行在这些子进程的工作线程中,导致追踪不到自定义函数调用。即使手动指定多线程模式,若yappi未在工作线程初始化,也无法捕获数据。
解决方法
方法1:代码内集成(单进程模式)
强制Uvicorn以单进程启动,并利用FastAPI的启动钩子在工作线程内初始化yappi:
import yappi from fastapi import FastAPI import uvicorn app = FastAPI() @app.on_event("startup") def init_tracing(): # 可选设置时钟类型:wall( wall-clock时间)或cpu(CPU时间) yappi.set_clock_type("wall") # 开启线程+协程追踪(若使用异步路由必须开协程追踪) yappi.start(trace_threads=True, trace_coroutines=True) @app.on_event("shutdown") def stop_tracing(): yappi.stop() # 输出函数调用统计 print("\n=== 函数调用统计 ===") yappi.get_func_stats().print_all() # 输出线程统计 print("\n=== 线程统计 ===") yappi.get_thread_stats().print_all() # 你的业务路由 @app.get("/api/test") async def test_business(): # 模拟业务逻辑 result = 0 for i in range(100000): result += i return {"result": result} if __name__ == "__main__": # 强制单进程启动Uvicorn uvicorn.run(app, host="0.0.0.0", port=8000, workers=1)
方法2:代码内集成(多进程模式)
若必须用多进程,需开启Uvicorn的preload参数,并在子进程初始化时启动yappi:
import yappi from fastapi import FastAPI import uvicorn import os app = FastAPI() def init_yappi(): yappi.set_clock_type("wall") yappi.start(trace_threads=True, trace_coroutines=True) # 检测当前进程是否是Uvicorn fork出的子进程 if os.getpid() != os.getppid(): init_yappi() @app.on_event("shutdown") def stop_tracing(): yappi.stop() yappi.get_func_stats().print_all() @app.get("/api/test") async def test_business(): result = 0 for i in range(100000): result += i return {"result": result} if __name__ == "__main__": # 多进程+预加载模式 uvicorn.run(app, host="0.0.0.0", port=8000, workers=2, preload=True)
方法3:CLI命令启动
使用yappi CLI时,需配合Uvicorn的单进程+预加载参数,确保追踪覆盖工作线程:
yappi -c wall uvicorn server.main:app --workers 1 --preload
关键注意点
- 若你的FastAPI路由使用异步函数,必须开启
trace_coroutines=True,否则yappi无法捕获协程内的函数调用。 - 不要仅在主线程启动yappi,必须在业务代码运行的线程/进程内完成追踪初始化。
- Uvicorn的
preload参数会让代码在fork子进程前加载,确保子进程能继承初始化逻辑。
内容的提问来源于stack exchange,提问作者SunnySlice
相关产品推荐
相关产品推荐

