
本文介绍如何在 python 项目中专业、可靠地将异常信息写入日志文件,涵盖结构化日志捕获、全局异常处理器、标准 logging 模块的正确用法及常见陷阱规避。
在实际 Python 开发中,仅靠 try/except 包裹整个 main() 是可行的起点,但远非最佳实践。真正健壮的日志方案应满足三个核心目标:完整上下文(含堆栈跟踪)、可追溯性(时间/模块/行号)、不影响程序语义(错误仍需传播)。以下为推荐的分层实现方式:
✅ 推荐做法:使用标准 logging 模块 + exception() 方法
避免自行拼接字符串或仅记录 str(e)——这会丢失关键堆栈信息。应统一使用 logging.exception(),它自动记录异常类型、消息和完整 traceback:
import logging
# 配置日志(建议在 main.py 开头执行一次)
logging.basicConfig(
level=logging.ERROR,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
handlers=[
logging.FileHandler('app.log', encoding='utf-8'),
logging.StreamHandler() # 同时输出到控制台(调试时有用)
]
)
logger = logging.getLogger(__name__)
def main():
# 你的主逻辑
risky_operation()
def risky_operation():
raise ValueError("示例异常:参数校验失败")
if __name__ == '__main__':
try:
main()
except Exception:
logger.exception("程序执行过程中发生未预期异常") # ← 关键:自动记录 traceback
raise # 重新抛出,确保调用方(如 systemd、CI 环境)能感知失败⚠️ 注意:logger.exception(msg) 等价于 logger.error(msg, exc_info=True),必须在 except 块内调用才有效。
? 进阶方案:注册全局异常钩子(兜底保障)
即使遗漏了某处 try/except,也可通过 sys.excepthook 捕获所有未处理异常:
import sys
import logging
def global_exception_handler(exc_type, exc_value, exc_traceback):
logger.critical(
"未捕获的全局异常",
exc_info=(exc_type, exc_value, exc_traceback)
)
sys.exit(1)
sys.excepthook = global_exception_handler此方式作为“最后一道防线”,不可替代业务逻辑中的针对性异常处理(如 FileNotFoundError 应重试或提示用户检查路径),但能杜绝日志空白。
立即学习“Python免费学习笔记(深入)”;
❌ 避免的反模式
- 仅记录 str(e) → 丢失堆栈、无法定位问题根源;
- except Exception: 后静默吞掉异常(无 raise/sys.exit) → 程序看似成功实则失败,破坏自动化流程可靠性;
- 每个函数都写独立 try/except 记录日志 → 日志重复、职责混乱,应遵循“谁引发、谁记录,或由上层统一捕获”原则;
- 日志文件未设置 encoding='utf-8' → 中文异常信息可能乱码。
? 总结
- 首选结构:if __name__ == '__main__': 中包裹 main() 的 try/except,配合 logging.exception() 和 raise;
- 必配基础:logging.basicConfig() 预设文件与控制台双输出、UTF-8 编码、带时间戳的格式;
- 增强保障:添加 sys.excepthook 作为兜底;
- 关键原则:日志是诊断工具,不是错误处理本身——异常该传播时必须传播,日志只是忠实记录者。










