asyncio环境下sentry必须用sentry_sdk.init()并启用enable_tracing=true,否则协程异常、后台任务错误等无法上报;需手动绑定isolation scope并显式调用capture_exception()捕获未await任务和线程异常。

asyncio 环境下 Sentry 初始化必须用 sentry_sdk.init() 而非 init() 的同步变体
Sentry 官方 SDK 默认以同步方式捕获异常,直接在 async def 函数中抛出的异常(尤其是未被 await 捕获的协程异常)会被忽略。必须显式启用异步支持,否则 asyncio.CancelledError、未 await 的任务崩溃、后台 task 异常全都不会上报。
正确做法是:在程序启动早期(如 if __name__ == "__main__": 块或 asyncio.run() 之前)调用 sentry_sdk.init(),并确保传入 enable_tracing=True 和 traces_sample_rate(即使只报错也建议开启 tracing,否则部分上下文丢失):
sentry_sdk.init(
dsn="https://xxx@o123.ingest.sentry.io/123",
enable_tracing=True,
traces_sample_rate=0.1,
environment="production",
)
漏掉 enable_tracing=True 是最常见错误——它不仅影响性能追踪,还决定 SDK 是否能挂载到 asyncio event loop 上监听 task 异常。
捕获未 await 的协程或 background task 异常需手动调用 sentry_sdk.capture_exception()
asyncio 中常见两类“逃逸”异常:
• 直接 create_task(func()) 后不 await,func 报错不会传播到主协程
• 使用 asyncio.to_thread() 或 loop.run_in_executor() 时,线程内异常无法自动捕获
这类场景必须包裹 try/except 并显式上报:
async def background_job():
try:
await some_unstable_api()
except Exception as e:
sentry_sdk.capture_exception(e) # 不要用 logging.exception()
<p>asyncio.create_task(background_job())</p>
- 不要依赖
logging.exception()—— Sentry 的日志集成默认不捕获非 root logger 的 exception,且不带 async 上下文 - 避免在 except 块里只写
raise:除非你确定上层会 await 并处理,否则异常仍会静默消失 - 对
run_in_executor,需在 executor 回调中捕获并用sentry_sdk.capture_exception(),不能靠外层 try
sentry_sdk.set_context() 在 async scope 下必须配合 sentry_sdk.get_isolation_scope()
异步函数之间共享全局 scope 会导致 context 污染(比如 A 请求设了 user_id=123,B 请求还没来得及设就被覆盖)。Sentry 的 isolation scope 是按协程隔离的,但默认不自动激活。
必须在每个 request/task 入口显式绑定:
async def handle_request(request):
scope = sentry_sdk.get_isolation_scope()
scope.set_context("request", {"path": request.path, "method": request.method})
scope.set_user({"id": request.user_id})
# 后续同协程内所有 capture_exception() 都自动带上该 context
await process_logic()
- 不要用
sentry_sdk.set_context()(它操作 global scope) - 不要在中间件或装饰器里用
with sentry_sdk.configure_scope()—— 这个上下文管理器不兼容 asyncio,可能跨协程泄漏 - FastAPI/Starlette 用户可直接用
scope.set_tag()补充 trace 标签,比 context 更轻量
本地开发时禁用 Sentry 需检查 SENTRY_DSN 和 environment 双重条件
仅靠 if os.getenv("ENV") != "prod" 关闭 init 很危险:一旦某处硬编码了 sentry_sdk.init(dsn="..."),就又启用了。更稳妥的方式是让 SDK 自己跳过:
sentry_sdk.init(
dsn=os.getenv("SENTRY_DSN"), # 本地设为空字符串或 None
environment=os.getenv("ENV", "local"),
# SDK 会自动忽略 dsn 为空的情况,无需 if 判断
)
- 如果
SENTRY_DSN环境变量未设置或为空,sentry_sdk.init()内部直接 return,不初始化任何 handler - 但若设置了无效 DSN(如 typo 的域名),SDK 仍会尝试连接并超时,拖慢启动——务必验证 DSN 格式
-
environment="local"不影响是否上报,只影响 Sentry 后台分组;真正开关是 DSN 值本身
实际部署时最容易被忽略的是 isolation scope 的手动绑定和 background task 的显式捕获——这两处不补,90% 的真实线上异步异常都会上报失败。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











