使用基本配置设置作为参数的日志装饰器

0 投票
1 回答
881 浏览
提问于 2025-04-17 22:44

编辑:我重新整理了问题,并添加了标题,希望这样更容易阅读。

问题

我想在 Python 装饰器库 中增加一些功能,特别是日志记录装饰器的功能。

我想添加的一个选项是通过提供一个字典来设置日志级别。但是,无论我设置什么级别,返回的结果总是一样的。


失败的测试

在运行下面的设置代码后,我通过运行以下代码来进行测试:

@log_with(setConfig={'level':logging.INFO})
def c(msg):
    print(msg)

c('OMG!!')

返回的结果是:

INFO:__main__:Running c


DEBUG:__main__:The following arguments have been received:# <-- This should not be here
('OMG!!',)

The following keyword arguments have been received:
{}


INFO:__main__:Returning c
OMG!!

我在使用 WinPython 2.7.6,方式是便携式的,没有注册,如果这有影响的话。测试在 qtconsole 中失败。


设置代码

import functools, logging


log = logging.getLogger(__name__)
log.setLevel(logging.DEBUG)

class log_with(object):
    '''Logging decorator that allows you to log with a
specific logger or set one up on the go.
'''

    def __init__(self,logger=None,funcentry='Running {}',funcexit='Returning {}',setConfig=None):

        self.logger        = logger
        self.ENTRY_MESSAGE = funcentry
        self.EXIT_MESSAGE  = funcexit
        self.setConfig     = setConfig


    def __call__(self, func):
        '''Returns a wrapper that wraps func.
The wrapper will log the entry and exit points of the function
with specified level.
'''
        # set logger if it was not set earlier
        if not self.logger:
            self.logger = logging.getLogger(func.__module__)
            logging.basicConfig(**self.setConfig)

        @functools.wraps(func)
        def wrapper(*args, **kwds):
            self.logger.info(self.ENTRY_MESSAGE.format(func.__name__)+'\n\n')
            self.logger.debug("The following arguments have been received:\n{}\n\nThe following keyword arguments have been received:\n{}\n\n".format(args,kwds))
            try:
                f_result = func(*args, **kwds)
                self.logger.info(self.EXIT_MESSAGE.format(func.__name__))
                return f_result
            except Exception:
                self.logger.exception("An exception was raised:\n\n")
        return wrapper

我想到的想法和尝试过的事情

重置所有处理程序

我尝试修改装饰器中的 if not self.logger 循环,去掉可能存在的所有处理程序,也就是:

....
if not self.logger:
    for handler in logging.root.handlers[:]:
        logging.root.removeHandler(handler)
....

参考了 这个回答,但这也没有效果,输出依然没有变化。

我不理解装饰器和/或日志模块!

我删除了

log = logging.getLogger(__name__)
log.setLevel(logging.DEBUG)

然后再次运行代码。这次屏幕上根本没有打印任何日志信息。对我来说,这意味着 if not self.logger 循环在 __call__ 方法中有问题,也就是说,日志记录器没有被创建。

不过我不知道为什么.....

1 个回答

0

这是我学到的内容:

  1. 看起来使用 basicConfig() 会设置一些全局的日志变量。改变日志级别似乎是全局生效的,而不仅仅是针对这一次调用。

  2. 实现装饰器时是用类还是用函数似乎会有区别。虽然我不太确定,但我觉得在 __init__ 中实现我的 self.setConfig,会影响我装饰的所有函数。我是根据我找到的解决方案推测的,那个方案是把装饰器实现为一个函数:

看起来有效的代码是这样的(虽然我对返回的顺序有点困惑):

def log_with_func(funcentry='Running {}',funcexit='Returning {}',setConfig=None):
    ENTRY_MESSAGE = funcentry
    EXIT_MESSAGE  = funcexit
    def func(f):
        @functools.wraps(f)
        def wrapper(*args, **kwds):
            for handler in logging.root.handlers[:]:
                logging.root.removeHandler(handler)
            logger = logging.getLogger(__name__)
            if setConfig:
                logging.basicConfig(**setConfig)
            else:
                logging.basicConfig(level=logging.INFO)
            logger.info(ENTRY_MESSAGE.format(f.__name__)+'\n\n')
            logger.debug("The following arguments have been received:\n{}\n\nThe following keyword arguments have been received:\n{}\n\n".format(args,kwds))
            try:
                f_result = f(*args, **kwds)
                logger.info(EXIT_MESSAGE.format(f.__name__))
                return f_result
            except Exception:
                logger.exception("An exception was raised:\n\n")
        return wrapper
    return func

应用后看起来是这样的:

In [24]: @log_with_func()
    ...: def aa(msg):
    ...:     print(msg + 'from a')
    ...:     

In [25]: @log_with_func(setConfig={'level':logging.DEBUG})
    ...: def bb(msg):
    ...:     print(msg + 'from b')
    ...:     

In [26]: print(aa('OMG!!!'))
    ...: print(bb('OMG!!!'))
    ...: print(aa('OMG!!!'))
    ...: 
INFO:__main__:Running aa


INFO:__main__:Returning aa
INFO:__main__:Running bb


DEBUG:__main__:The following arguments have been received:
('OMG!!!',)

The following keyword arguments have been received:
{}


INFO:__main__:Returning bb
INFO:__main__:Running aa


INFO:__main__:Returning aa
OMG!!!from a
None
OMG!!!from b
None
OMG!!!from a
None

In [27]: 

撰写回答