You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python自定义异常日志:异常钩子返回错误函数信息的解决

解决sys.excepthook日志显示异常抛出位置的问题

问题原因

当你在自定义的sys.excepthook函数中直接调用logging.error()等方法时,logging模块默认会捕捉当前函数(即exception_hook)的调用栈信息,因此日志里显示的是这个函数的文件名、行号,而非原异常抛出的真实位置。

解决方法

方法1:用logging.exception自动捕获异常栈

logging.exception()会自动附带完整的异常栈追踪,并且能正确指向原异常的抛出位置,是最简便的解决方案:

import sys
import logging

# 配置日志格式,包含文件名、函数名、行号
logging.basicConfig(
    format='%(asctime)s - %(levelname)s - %(filename)s:%(funcName)s:%(lineno)d - %(message)s',
    level=logging.INFO
)

def exception_hook(exc_type, exc_value, exc_traceback):
    # 用exception方法记录,自动包含异常栈,位置信息会指向原异常抛出点
    logging.exception("未捕获异常", exc_info=(exc_type, exc_value, exc_traceback))

sys.excepthook = exception_hook

# 测试代码
def trigger_error():
    raise ValueError("测试自定义异常日志格式")

trigger_error()

方法2:手动提取原异常的栈帧信息

如果需要更精细控制日志内容,可以通过exc_traceback提取原异常的栈帧数据,用extra参数覆盖logging默认的位置字段:

import sys
import logging
import traceback

logging.basicConfig(
    format='%(asctime)s - %(levelname)s - %(filename)s:%(funcName)s:%(lineno)d - %(message)s',
    level=logging.INFO
)

def exception_hook(exc_type, exc_value, exc_traceback):
    if exc_traceback is None:
        logging.error(f"未捕获异常: {exc_value}")
        return
    
    # 获取原异常的栈帧信息,跳过hook函数的层级
    tb_frame = exc_traceback.tb_frame.f_back
    filename = tb_frame.f_code.co_filename
    func_name = tb_frame.f_code.co_name
    line_no = exc_traceback.tb_lineno

    # 手动传入原异常的位置信息
    logging.error(
        f"未捕获异常: {exc_value}",
        exc_info=(exc_type, exc_value, exc_traceback),
        extra={
            'filename': filename,
            'funcName': func_name,
            'lineno': line_no
        }
    )

sys.excepthook = exception_hook

# 测试代码
def trigger_error():
    raise ValueError("测试自定义异常日志格式")

trigger_error()

方法3:自定义Logger修改栈查找深度

通过继承logging.Logger,修改findCaller方法的栈查找层级,跳过exception_hook函数的栈帧:

import sys
import logging

class CustomLogger(logging.Logger):
    def findCaller(self, stack_info=False, stacklevel=1):
        # 额外跳过1层栈帧(对应exception_hook函数)
        return super().findCaller(stack_info, stacklevel + 1)

# 设置全局Logger类为自定义类
logging.setLoggerClass(CustomLogger)

logging.basicConfig(
    format='%(asctime)s - %(levelname)s - %(filename)s:%(funcName)s:%(lineno)d - %(message)s',
    level=logging.INFO
)

def exception_hook(exc_type, exc_value, exc_traceback):
    logging.error(
        f"未捕获异常: {exc_value}",
        exc_info=(exc_type, exc_value, exc_traceback)
    )

sys.excepthook = exception_hook

# 测试代码
def trigger_error():
    raise ValueError("测试自定义异常日志格式")

trigger_error()

注意事项

  • 若需要完整的异常栈追踪,优先用方法1;若只需显示原异常位置信息,方法2或3更灵活。
  • 实际使用中建议对exc_traceback做非空判断,避免极端场景下报错。

内容的提问来源于stack exchange,提问作者qalis

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 17:10:24