asyncio任务崩溃默认不打印堆栈,因未await的任务异常被静默捕获并仅存于task.exception();set_exception_handler仅处理未await且未调用exception()/result()的已结束任务异常。

asyncio 任务崩溃时默认不打印堆栈,为什么?
Python 的 asyncio.create_task() 或 asyncio.ensure_future() 启动的任务,如果协程内部抛出未捕获异常,**不会像同步代码那样中断主线程或打印 traceback**。异常会被静默吞掉,只在任务对象的 exception() 方法里可查——这导致调试时完全看不到错在哪。
set_exception_handler 能捕获哪些异常?
它只捕获「未被 await 的任务」中抛出的未处理异常(即任务已结束但没人调用 result() 或 exception())。换句话说:只要你在启动任务后没显式等待或检查它,异常就会落到 handler 里。
- ✅ 捕获:
asyncio.create_task(coro())启动后就丢着不管,coro 内部报错 - ❌ 不捕获:
await asyncio.create_task(coro())—— 异常会直接向上冒泡,由外层 try/catch 处理 - ❌ 不捕获:任务虽未 await,但你后续调用了
task.exception()—— 此时异常已被“取走”,不会进 handler
怎么写一个真正有用的 exception handler?
别只 print,要保留完整 traceback 和任务上下文。默认 handler 只输出简短信息,容易漏掉关键线索。
import asyncio
import traceback
<p>def custom_exc_handler(loop, context):</p><div class="aritcle_card flexRow artxards">
<div class="artcardd flexRow">
<a class="aritcle_card_img" rel="nofollow" href="/xiazai/skill6591" title="Li Python Sec Check"><img
src="https://img.php.cn/upload/skill/000/000/081/179102166033725.jpg" alt="Li Python Sec Check" onerror="this.onerror='';this.src='/static/lhimages/moren/morentu.png'" ></a>
<div class="aritcle_card_info flexColumn">
<a rel="nofollow" href="/xiazai/skill6591" title="Li Python Sec Check" class="overflowclass">Li Python Sec Check</a>
<p class="overflowclass">Python 安全规范检查工具:基于 CloudBase 规范、腾讯安全指南,LLM 智能分析(默认禁用,优先本地执行)</p>
</div>
<a rel="nofollow" href="/xiazai/skill6591" title="Li Python Sec Check" class="aritcle_card_btn flexRow flexcenter"><b></b><span>下载</span>
</a>
</div>
</div><h1>先让默认逻辑做基础处理(比如 logging)</h1><pre class="brush:php;toolbar:false;">loop.default_exception_handler(context)
# 补充:如果有 task 且异常未被取走,手动 dump traceback
if 'task' in context and (exc := context.get('exception')):
print(f"\n? Unhandled exception in task: {context['task']}")
traceback.print_exception(type(exc), exc, exc.__traceback__)loop = asyncio.get_event_loop() loop.set_exception_handler(custom_exc_handler)
注意:context 字典里不一定有 'exception' 键(比如 CancelledError 可能不带),所以要用 .get() 安全访问。
常见陷阱:handler 里再抛异常会怎样?
如果自定义 handler 本身出错(比如误写了 traceback.print_exc() 却没传参数),asyncio 会 fallback 到 sys.excepthook,而且**不会再次调用你的 handler**——等于静默失败。更糟的是,如果 handler 抛的是 SystemExit 或 KeyboardInterrupt,整个事件循环可能意外终止。
- 务必在 handler 内部包 try/except,尤其对
traceback.print_exception()这类操作 - 避免在 handler 里 await 任何协程(loop 可能正在关闭,await 会卡死或报
RuntimeError: Event loop is closed) - 不要在 handler 里调用
loop.stop()或loop.close()—— 这会让后续任务无法清理
最稳妥的做法是:只做日志记录、dump traceback、发告警,其他事交给外部监控系统。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










