logging.exception必须在except块内调用,否则因无活跃异常导致堆栈丢失;它等价于logging.error(..., exc_info=True),是语法糖;需确保formatter含%(exc_text)s且handler级别≥ERROR。

logging.exception 为什么必须在异常处理块里用
logging.exception 不是万能错误捕获器,它只在当前有活跃异常(即 sys.exc_info() 非空)时才有效输出堆栈。如果在 try 块外、或 except 外直接调用,日志里只会写 “NoneType: None”,堆栈完全丢失。
常见错误现象:Exception: None 或日志中看不到 traceback。
- 必须在
except子句内调用,且不能先raise或return破坏异常上下文 - 不能和
logging.error(..., exc_info=True)混用——两者效果相同,重复写会打两遍堆栈 - 如果想在捕获后继续抛出,要用
raise(不带参数),保持原始异常链;用raise e会丢失 traceback
logging.exception 和 logging.error(..., exc_info=True) 的区别
二者底层完全等价:logging.exception(msg) 就是 logging.error(msg, exc_info=True) 的语法糖。没有功能差异,只有可读性偏好。
使用场景:团队约定优先用 exception 表明“此处明确为异常记录”,比手动传 exc_info=True 更直观。
立即学习“Python免费学习笔记(深入)”;
-
logging.exception("数据库查询失败")→ 清晰表达意图 -
logging.error("数据库查询失败", exc_info=True)→ 等效但略冗长 -
logging.error("数据库查询失败")→ ❌ 不带exc_info,堆栈不会出现
自定义 logger 时 exception() 仍需注意 root logger 配置
即使你用了 logger = logging.getLogger("myapp"),logger.exception() 能否打出堆栈,还取决于 handler 是否启用 traceback 格式支持——其实不是 handler 决定,而是 formatter 中是否包含 %(exc_text)s 或使用了默认格式(默认已支持)。
容易踩的坑:自己写了极简 formatter,比如 %(asctime)s %(message)s,结果堆栈消失。
- 推荐始终用默认 formatter,或显式包含
%(exc_text)s - 确认 handler 的
level≥ERROR(exception发的是 ERROR 级别) - 若用
RotatingFileHandler等,无需额外配置,堆栈自动追加到日志行末
在多线程/协程中 exception() 还安全吗
安全。Python 的 logging 模块本身是线程安全的,exception() 获取的是当前线程的 sys.exc_info(),不会错乱。
但 asyncio 场景要小心:在 async def 里直接用 exception() 是无效的——因为异常通常不在主线程触发,且 sys.exc_info() 在协程挂起后可能被覆盖。
- 在
except块中同步调用logger.exception()是 OK 的 - 不要在
asyncio.create_task()外围裸包一层就以为能捕获子任务异常;子任务异常默认静默,需用asyncio.gather(..., return_exceptions=True)或在 task 内部try/except - 异步框架如 FastAPI,应通过中间件或
exception_handler统一捕获,而非零散用exception()
try/except 里只 print 了点东西,却没调用 logger.exception,或者调了但 log level 被设成 WARNING,导致 ERROR 级日志根本没输出。


















