gpt4 book ai didi

python - 在 Python 记录器中覆盖 lineno 的最佳方法

转载 作者:太空狗 更新时间:2023-10-29 18:30:25 26 4
gpt4 key购买 nike

我编写了一个装饰器,用于记录用于调用特定函数或方法的参数。如下所示,除了 logRecord 中报告的行号是装饰器的行号而不是被包装的 func 的行号外,它运行良好:

from functools import wraps
import inspect
import logging

arg_log_fmt = "{name}({arg_str})"


def log_args(logger, level=logging.DEBUG):
"""Decorator to log arguments passed to func."""
def inner_func(func):
line_no = inspect.getsourcelines(func)[-1]

@wraps(func)
def return_func(*args, **kwargs):
arg_list = list("{!r}".format(arg) for arg in args)
arg_list.extend("{}={!r}".format(key, val)
for key, val in kwargs.iteritems())
msg = arg_log_fmt.format(name=func.__name__,
arg_str=", ".join(arg_list))
logger.log(level, msg)
return func(*args, **kwargs)
return return_func

return inner_func

if __name__ == "__main__":
logger = logging.getLogger(__name__)
handler = logging.StreamHandler()
fmt = "%(asctime)s %(levelname)-8.8s [%(name)s:%(lineno)4s] %(message)s"
handler.setFormatter(logging.Formatter(fmt))
logger.addHandler(handler)
logger.setLevel(logging.DEBUG)


@log_args(logger)
def foo(x, y, z):
pass

class Bar(object):
@log_args(logger)
def baz(self, a, b, c):
pass

foo(1, 2, z=3)
foo(1, 2, 3)
foo(x=1, y=2, z=3)

bar = Bar()
bar.baz(1, c=3, b=2)

此示例产生以下输出

2015-09-07 12:42:47,779 DEBUG    [__main__:  25] foo(1, 2, z=3)
2015-09-07 12:42:47,779 DEBUG [__main__: 25] foo(1, 2, 3)
2015-09-07 12:42:47,779 DEBUG [__main__: 25] foo(y=2, x=1, z=3)
2015-09-07 12:42:47,779 DEBUG [__main__: 25] baz(<__main__.Bar object at 0x1029094d0>, 1, c=3, b=2)

请注意,行号都指向装饰器。

使用 inspect.getsourcelines(func) 我可以获得我感兴趣的行号,但是试图在 logger 中覆盖 lineno。 debug 导致错误。让包装函数的行号出现在日志记录语句中的最佳方法是什么?

最佳答案

这是一篇旧帖子,但这个答案可能对其他人仍然有用。

现有解决方案的一个问题是有 multiple parameters providing logging context ,如果你想支持任意日志格式化程序,所有这些都需要修补。

原来这是raised as an issue with the Python logging library about a year ago ,因此,the stacklevel keyword argument was added in Python 3.8 .使用该功能,您只需修改日志记录调用以将堆栈级别设置为 2(在您的示例中调用 logger.log 的位置之上的级别):

logger.log(level, msg, stacklevel=2)

由于 Python 3.8 尚未发布(在此回复时),您可以使用 findCaller and _log methods updated in Python 3.8 猴子修补您的记录器.

我有一个名为 logquacious 的日志实用程序库,我在那里做同样的猴子修补。您可以重复使用 patch_logger class that I've defined in logquacious并更新上面的日志记录示例:

from logquacious.backport_configurable_stacklevel import patch_logger

logger = logging.getLogger(__name__)
logger.__class__ = patch_logger(logger.__class__)

如 unutbu 的回答中所述,在使用范围之外撤消此猴子修补可能是个好主意,该文件中的其他一些代码就是这样做的。

关于python - 在 Python 记录器中覆盖 lineno 的最佳方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32443808/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com