我正在开发一个Python程序,我需要记录所有异常(使用clickhouse上的自定义记录器),包括它们的上下文,以帮助调试。我没有简单地记录导致问题的最终函数,而是实现了一个 CustomException 类来存储整个异常路径,包括调用哪个 API、执行的 CRUD 操作以及任何相关参数等详细信息。
这是实现的简化版本:
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
虽然硬编码这个例外,但一切正常。但将其添加到所有 CRUD 函数中似乎有点结束了。
为了跨多个函数自动执行此异常日志记录过程,我创建了一个名为 auto_logger 的装饰器。该装饰器包装整个函数,拦截任何异常,并将它们添加到 CustomException 实例中。
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
# Handle CustomException
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
我遇到的问题是,将此装饰器应用于函数后,ex.traces 列表似乎保留了以前的异常,导致意外的行为。这是一个示例日志:
api/foo - crud_foo.change_bar - ERROR - Error validating bar [Error Code: 69420]
api/foo - crud_foo.change_bar - DEBUG - "some value"
api/foo - crud_foo.change_bar - TRACE - crud_foo.change_bar
api/foo - /prefix/change_bar - TRACE - change_bar
第二次通话后:
api/foo - crud_foo.change_password - ERROR - Ошибка валидации пароля [Error Code: 69420]
api/foo - crud_foo.change_password - DEBUG - "some value"
api/foo - crud_foo.change_password - TRACE - change_password
api/foo - crud_foo.change_password - ERROR - Ошибка валидации пароля [Error Code: 69420]
api/foo - crud_foo.change_password - DEBUG - "some value"
api/foo - crud_foo.change_password - TRACE - change_password
api/foo - /prefix/change_bar - TRACE - change_bar
其他调用也会发生同样的情况,成堆的旧日志位于新调用下方。
任何关于为什么 ex.traces 列表会累积以前的异常的见解将不胜感激。
实现装饰器后,异常开始堆积。我尝试删除
ex
和error
。
我尝试重新初始化记录器(毕竟这不是问题)。
只有重新加载我的服务器才会将其返回到一个跟踪、第二个函数调用,之后仍然收集异常。
我希望有一些方法可以刷新内存,或者有一个装饰器可以在每次使用后重新初始化被装饰的函数或装饰器本身。
问我自己的问题:
except CustomException as ex
在装饰器中保留旧的ex.traces
的原因是wrapper
随着程序启动而初始化,并在整个生命周期中保持静态值。附加 ex.traces
会保留旧痕迹,因为它没有重新初始化。
在
__init__
期间我应该写self.traces = []
class CustomException(BaseException):
error_code: int
value: Any
traces: list[ExceptionSnippet] = []
def __init__(self, arg=None):
self.traces = [] # new
if isinstance(arg, CustomException):
self.error_code = arg.error_code
self.value = arg.value
self.traces = arg.traces
以前这不是问题,因为
try: ... except: ...
是函数的一部分,每次调用都会初始化 CustomException。
没想到会有这样的效果。对我来说是一个惊喜...