Python自定义异常Trace列表累积旧异常问题求助
问题描述
我正在开发一个Python程序,需通过ClickHouse自定义日志记录所有异常及其上下文以辅助调试。为此实现了CustomException类存储完整异常路径(含API调用、CRUD操作及参数等),并通过auto_logger装饰器自动化该异常日志流程。但应用装饰器后发现,CustomException的traces列表会保留之前的异常,导致日志中旧异常不断累积。尝试过删除异常实例、重新初始化日志,但仅重启服务器能暂时解决,再次调用后问题复现。现求助排查该累积问题的原因及解决方法,期望找到内存清理或装饰器重初始化的方案。
相关代码实现如下:
class ExceptionSnippet: def __init__(self, file_name, func_name, msg, error_level): self.file_name = file_name self.func_name = func_name self.msg = msg self.error_level = error_level class CustomException(BaseException): error_code: int value: Any traces: list[ExceptionSnippet] = [] def __init__(self, arg=None): if isinstance(arg, CustomException): self.error_code = arg.error_code self.value = arg.value self.traces = arg.traces
def auto_logger(file_name, func_name, error_code): def decorator(func): def wrapper(*args, **kwargs): try: return func(*args, **kwargs) except CustomException as ex: ex.traces.append( ExceptionSnippet( file_name, func_name, ex.traces[-1].func_name, 'trace' )) raise ex except Exception as ex: error = CustomException() error.error_code = error_code error.traces.append(ExceptionSnippet( file_name, func_name, ex.args[0], 'error' )) raise error return wrapper return decorator
问题原因
核心问题出在CustomException类的traces属性定义上——它被声明为类属性,而非实例属性。在Python中,类属性是所有实例共享的,所有CustomException实例都会指向同一个列表对象:
- 第一次创建实例并向
traces添加内容时,这个列表会被修改,后续所有新创建的实例(包括通过CustomException(arg)复制的实例)都会复用这个已修改的类属性列表,导致旧异常痕迹不断累积。 - 即使删除单个异常实例,类属性本身依然存在,所以重启服务器前,这个共享列表的状态不会被重置。
解决方法
1. 将traces改为实例属性
修改CustomException的定义,把traces移到__init__方法中,确保每个实例都拥有独立的列表:
class CustomException(BaseException): error_code: int value: Any def __init__(self, arg=None): # 每个实例初始化独立的traces列表 self.traces: list[ExceptionSnippet] = [] if isinstance(arg, CustomException): self.error_code = arg.error_code self.value = arg.value # 复制传入实例的traces,而非直接引用 self.traces = arg.traces.copy()
使用arg.traces.copy()是为了避免复制实例时共享同一个列表,确保每个实例的traces都是独立副本。
2. 验证装饰器逻辑
修改后,装饰器中的异常处理逻辑无需额外调整,因为每个CustomException实例的traces都是独立的:
def auto_logger(file_name, func_name, error_code): def decorator(func): def wrapper(*args, **kwargs): try: return func(*args, **kwargs) except CustomException as ex: ex.traces.append( ExceptionSnippet( file_name, func_name, ex.traces[-1].func_name, 'trace' )) raise ex except Exception as ex: error = CustomException() error.error_code = error_code error.traces.append(ExceptionSnippet( file_name, func_name, ex.args[0], 'error' )) raise error return wrapper return decorator
3. 效果确认
修改后:
- 新创建的
CustomException实例不会携带之前异常的痕迹 - 复制实例时会创建
traces的副本,不会影响原实例的列表 - 无需重启服务器,每次请求的异常日志都会独立存储
内容的提问来源于stack exchange,提问作者Miky Mike
相关产品推荐
相关产品推荐

