使用基本配置设置作为参数的日志装饰器
编辑:我重新整理了问题,并添加了标题,希望这样更容易阅读。
问题
我想在 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
这是我学到的内容:
看起来使用
basicConfig()
会设置一些全局的日志变量。改变日志级别似乎是全局生效的,而不仅仅是针对这一次调用。实现装饰器时是用类还是用函数似乎会有区别。虽然我不太确定,但我觉得在
__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]: