python

关注公众号 jb51net

关闭
首页 > 脚本专栏 > python > Python traceback获取异常信息

Python使用traceback模块获取详细异常信息的方法

作者:星河耀银海

本文详解Python中traceback模块的核心功能,包括获取、打印和记录异常调用栈信息,支持限制深度、构建自定义traceback及实现优雅错误页面,并介绍全局异常处理实战,强调程序化访问异常堆栈,需要的朋友可以参考下

一、开篇:不只是"出了什么错",还要"在哪里出的错"

当你看到一个异常时,Python自动打印的"Traceback (most recent call last)…"就是traceback——调用栈的追踪信息。traceback模块让你能程序化地获取和处理这些追踪信息——记录到日志、发送告警、展示给用户。

基本使用:

import traceback

def level3():
    """最深层——故意出错"""
    return 1 / 0  # ZeroDivisionError

def level2():
    return level3()

def level1():
    return level2()

# 捕获并获取traceback
try:
    level1()
except ZeroDivisionError:
    # 获取traceback字符串
    tb_str = traceback.format_exc()
    print("捕获到的traceback:")
    print(tb_str)
    # Traceback (most recent call last):
    #   File "...", line ..., in <module>
    #     level1()
    #   File "...", line ..., in level1
    #     return level2()
    #   ...
    # ZeroDivisionError: division by zero

二、traceback的核心功能

2.1 获取traceback信息

import traceback
import sys

try:
    1 / 0
except ZeroDivisionError:
    # 方式一:format_exc() —— 获取当前异常的traceback字符串
    tb_string = traceback.format_exc()
    print("=== format_exc() ===")
    print(tb_string)

    # 方式二:手动获取(更灵活)
    exc_type, exc_value, exc_tb = sys.exc_info()
    print(f"\n异常类型: {exc_type.__name__}")
    print(f"异常消息: {exc_value}")

    # 从traceback对象中提取信息
    tb_list = traceback.extract_tb(exc_tb)
    print("\n调用栈:")
    for i, frame in enumerate(tb_list):
        print(f"  #{i}: {frame.filename}")
        print(f"      行{frame.lineno}: {frame.line}")
        print(f"      函数: {frame.name}")

    # 格式化单个traceback
    formatted = traceback.format_exception(exc_type, exc_value, exc_tb)
    print("\n=== format_exception() ===")
    print("".join(formatted))

2.2 打印和记录

import traceback
import logging

# 配置日志
logging.basicConfig(
    level=logging.ERROR,
    format='%(asctime)s [%(levelname)s] %(message)s',
    filename='app.log'
)

def risky_operation():
    """可能失败的操作"""
    data = {"key": None}
    return data["key"].upper()  # AttributeError!

try:
    risky_operation()
except Exception:
    # print_exc() —— 直接打印到stderr
    print("打印到stderr:")
    traceback.print_exc()

    # 记录到日志
    print("记录到日志文件...")
    logging.error("操作失败", exc_info=True)
    # 这会记录完整的traceback到app.log

    # 获取字符串自己处理
    tb_string = traceback.format_exc()
    # 发送到监控系统、写入数据库等
    # send_to_monitoring(tb_string)

三、高级用法

3.1 限制traceback深度

import traceback

def func_a():
    func_b()

def func_b():
    func_c()

def func_c():
    1 / 0  # 错误在这里

try:
    func_a()
except ZeroDivisionError:
    # 只显示最后几层调用栈
    tb_string = traceback.format_exc(limit=2)
    print("限制2层:")
    print(tb_string)
    # 只显示func_c和func_b

    # 只显示异常信息(不显示调用栈)
    print("\n只显示异常:")
    traceback.print_exc(limit=0)

3.2 创建自定义traceback

import traceback
import sys

class DetailedError(Exception):
    """携带详细上下文的异常"""
    def __init__(self, message, context=None):
        super().__init__(message)
        self.context = context or {}
        # 自动捕获调用栈
        self.traceback = traceback.format_exc() if sys.exc_info()[0] else ""

def process_order(order_data):
    """处理订单——在异常上附加业务上下文"""
    try:
        validate_order(order_data)
    except ValueError as e:
        raise DetailedError(
            f"订单处理失败: {e}",
            context={
                "order_id": order_data.get("id"),
                "customer": order_data.get("customer"),
                "items_count": len(order_data.get("items", [])),
            }
        ) from e

def validate_order(data):
    if not data.get("items"):
        raise ValueError("订单没有商品")

# 使用
try:
    process_order({"id": "ORD-001", "customer": "张三", "items": []})
except DetailedError as e:
    print(f"错误: {e}")
    print(f"上下文: {e.context}")
    print(f"Traceback: {e.traceback[:100]}...")

3.3 优雅的错误页面

import traceback
import sys
import datetime

def format_error_for_user(exc_type, exc_value, exc_tb):
    """将异常格式化为用户友好的错误报告"""
    # 提取traceback帧
    frames = traceback.extract_tb(exc_tb)

    # 找到错误的根本位置(最后一帧)
    last_frame = frames[-1]

    report = f"""
╔══════════════════════════════════════════╗
║           程序运行错误                      ║
╠══════════════════════════════════════════╣
║ 错误类型: {exc_type.__name__:<31} ║
║ 错误消息: {str(exc_value)[:31]:<31} ║
╠══════════════════════════════════════════╣
║ 发生位置:                                 ║
║   文件: {last_frame.filename[-30:]:<30} ║
║   行号: {last_frame.lineno:<31} ║
║   函数: {last_frame.name:<31} ║
║   代码: {last_frame.line[:30]:<30} ║
╠══════════════════════════════════════════╣
║ 时间: {datetime.datetime.now().isoformat()[:19]}               ║
╚══════════════════════════════════════════╝
"""
    return report

# 使用
try:
    data = [1, 2, 3]
    value = data[10]  # IndexError
except IndexError:
    exc_type, exc_value, exc_tb = sys.exc_info()
    print(format_error_for_user(exc_type, exc_value, exc_tb))

四、实战:全局异常处理

import traceback
import sys
import logging
from datetime import datetime

class ErrorTracker:
    """全局错误追踪器"""

    def __init__(self, log_file="errors.log"):
        self.error_count = 0
        self.errors = []

        # 配置日志
        logging.basicConfig(
            filename=log_file,
            level=logging.ERROR,
            format='%(asctime)s [%(levelname)s] %(message)s'
        )

    def capture(self, exc_type, exc_value, exc_tb):
        """捕获并记录异常"""
        self.error_count += 1

        # 提取信息
        frames = traceback.extract_tb(exc_tb)
        tb_string = "".join(
            traceback.format_exception(exc_type, exc_value, exc_tb)
        )

        # 构建错误记录
        error_record = {
            "id": self.error_count,
            "type": exc_type.__name__,
            "message": str(exc_value),
            "location": f"{frames[-1].filename}:{frames[-1].lineno}"
                        if frames else "未知",
            "time": datetime.now().isoformat(),
            "traceback": tb_string,
        }

        self.errors.append(error_record)

        # 记录到日志
        logging.error(
            f"[#{error_record['id']}] {error_record['type']}: "
            f"{error_record['message']} "
            f"({error_record['location']})"
        )

        return error_record

    def get_summary(self):
        """获取错误摘要"""
        return {
            "total_errors": self.error_count,
            "by_type": {},
            "recent": self.errors[-5:],
        }

# 全局异常处理器
tracker = ErrorTracker()

def install_global_handler():
    """安装全局异常处理器"""
    def handler(exc_type, exc_value, exc_tb):
        error = tracker.capture(exc_type, exc_value, exc_tb)
        print(f"\n⚠ 发生错误 #{error['id']}: {error['type']}")
        print(f"   位置: {error['location']}")
        print(f"   消息: {error['message']}")

    sys.excepthook = handler

# install_global_handler()
# 之后所有未被捕获的异常都会进入ErrorTracker

五、总结

traceback模块让你能程序化地访问异常的调用栈信息——不只是让Python打印到控制台。

核心函数速查:

函数用途
traceback.format_exc()获取当前异常的traceback字符串
traceback.print_exc()打印到stderr
traceback.extract_tb(tb)提取traceback帧信息
traceback.format_exception(type, val, tb)格式化异常为字符串列表
sys.exc_info()获取当前异常的三元组

最佳实践:用logging.exception()或logging.error(msg, exc_info=True)来记录异常——它们自动包含完整的traceback。

以上就是Python使用traceback模块获取详细异常信息的方法的详细内容,更多关于Python traceback获取异常信息的资料请关注脚本之家其它相关文章!

您可能感兴趣的文章:
阅读全文