asyncio.debug=true 能暴露协程创建位置(如main.py:42)、每个await挂起点(xxx.py:88)、任务调度时间戳与上下文,从而串联断裂的执行流;但会降低性能,仅限开发/测试启用。

因为协程的执行被事件循环切片调度,原始调用栈在 await 切换时被截断,导致 traceback 里看不到上层调用位置,只剩孤零零的 await 行和异常抛出处。
asyncio.debug=True 能暴露哪些隐藏信息?
开启后,asyncio.run(main(), debug=True) 不仅打印异常本身,还会标注:
- 协程创建时的完整文件路径与行号(比如
main.py:42) - 每个
await挂起点(awaiting at xxx.py:88) - 任务被调度进事件循环的时间戳与上下文
这些信息能帮你把断裂的执行流重新串起来。但注意:debug 模式会显著降低性能,仅限开发/测试环境启用。
未 await 的 task 异常为什么完全不显示?
当你调用 asyncio.create_task() 却没 await 它或调用 task.result(),这个 task 就成了“孤儿”。它的异常不会冒泡到主流程,也不会触发 sys.excepthook —— 除非你显式设置异常处理器:
def exception_handler(loop, context):
exc = context.get("exception")
if exc:
print("Uncaught in task:", exc)
loop.set_exception_handler(exception_handler)
否则,Task exception was never retrieved 这类警告都可能被忽略,尤其在日志级别设为 WARNING 以上时。
contextvars + logging 如何补全上下文链路?
异步任务跨调度周期执行,传统线程局部变量(threading.local)失效。必须用 contextvars 绑定 trace_id、user_id 等关键字段:
- 在入口处创建
ContextVar并 set 值 - 所有子协程通过
contextvar.get()获取,写入日志 formatter - 异常捕获时,把
contextvar.get()和 traceback 一起输出
否则,你看到的只是 “ValueError: timeout”,却不知道它来自哪个用户、哪次请求、哪个重试轮次。
真正难的不是堆栈本身,而是协程生命周期和异常传播路径不匹配——你得同时盯住事件循环状态、task 生命周期、上下文变量绑定点,三者缺一不可。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











