阅读Python的logging
库(2.7版)的文档,我遇到了以下内容:
Logger.debug(msg,* args,** kwargs)
[...]第二个关键字参数是extra,可用于传递字典,该字典用于填充为具有用户定义属性的日志记录事件创建的LogRecord的
__dict__
。然后可以根据需要使用这些自定义属性。例如,它们可以合并到记录的消息中。 [...] 字典中传入的字符串中的密钥不应与日志系统使用的密钥冲突。 [emph。矿]
那为什么存在这种约束?在我看来,这没有任何理由从库中删除灵活性(由开发人员来检查哪些键是内置的,哪些不是内置的。)
想象一下,你想写一个记录函数入口和退出的装饰器:
def log_entry_exit(func):
def wrapper(*args, **kwargs):
logger.debug('Entry')
result = func(*args, **kwargs)
logger.debug('Exit')
return result
return wrapper
@log_entry_exit
def foo():
pass
假设您还想记录封闭函数的名称:
format_string = '%(funcName)s: %(message)s'
糟糕!这不起作用。输出是:
>>> foo()
wrapper: Entry
wrapper: Exit
当然,函数名称的计算结果为wrapper
,因为这是封闭函数。然而,这不是我想要的。我想要打印装饰函数的函数名称。因此,将我的日志记录调用修改为:
logger.debug('<msg>', extra={'funcName': func.__name__})
但是(正如文档已经指出的那样)这不起作用:
KeyError: "Attempt to overwrite 'funcName' in LogRecord"
然而,对于给定的问题,这将是一个非常简单明了的解决方案。
再次,为什么logging
阻止我为内置属性设置自定义值?
答案 0 :(得分:4)
不是作者,我不能确定,但我有预感。
看着 https://hg.python.org/cpython/file/3.5/Lib/logging/init.py,这似乎是抛出引用错误的代码:
rv = _logRecordFactory(name, level, fn, lno, msg, args, exc_info, func, sinfo)
if extra is not None:
for key in extra:
if (key in ["message", "asctime"]) or (key in rv.__dict__):
raise KeyError("Attempt to overwrite %r in LogRecord" % key)
rv.__dict__[key] = extra[key]
查看该文件中的_ _ init _ _()方法,我们可以看到它设置了一长串属性列表,其中至少有一些属性用于跟踪对象状态(从其他地方借用术语,这些用于私有成员变量的目的):
self.args = args
self.levelname = getLevelName(level)
self.levelno = level
self.pathname = pathname
try:
self.filename = os.path.basename(pathname)
self.module = os.path.splitext(self.filename)[0]
except (TypeError, ValueError, AttributeError):
self.filename = pathname
self.module = "Unknown module"
self.exc_info = exc_info
self.exc_text = None # used to cache the traceback text
self.stack_info = sinfo
self.lineno = lineno
self.funcName = func
[...]
代码在各个地方做出假设,这些属性包含它们被初始化为包含的内容;正如我们在上面看到的那样,它不是在每次使用它时都在防御性地检查该值是否仍然合理,而是阻止尝试更新它们中的任何一个。并且,它不是试图区分“安全覆盖”和“不安全覆盖”属性,而是简单地阻止任何覆盖。
在funcName的特定情况下,我怀疑你不会因为覆盖它而遭受任何不良影响(除了显示不同的funcName)。
可能的前进方式:
当然,无论你做什么,都要仔细测试你的修改以避免意外。
答案 1 :(得分:1)
我知道这已经有几年历史了,但是没有选择的答案。如果遇到任何其他问题,我有一个解决方法,该方法应在日志记录模块进行更改时继续工作。
不幸的是,作者并没有以易于检查的方式公开可能会冲突的密钥。但是,他/她确实在文档中提示了一种方法。这一行:https://hg.python.org/cpython/file/3.5/Lib/logging/init.py#l368返回LogRecord对象的外壳:
rv = _logRecordFactory(None, None, "", 0, "", (), None, None)
...在此对象中,您可以看到所有属性,并且可以制作一个{<1>},其中包含“冲突键” 。
我创建了日志记录帮助程序模块:
Set
......只是在冲突的键前加上import logging
clashing_keywords = {key for key in dir(logging.LogRecord(None, None, "", 0, "", (), None, None)) if "__" not in key}
additional_clashing_keywords = {
"message",
"asctime"
}
clashing_keywords = clashing_keywords.union(additional_clashing_keywords)
def make_safe_kwargs(kwargs):
'''
Makes sure you don't have kwargs that might conflict with
the logging module
'''
assert isinstance(kwargs, dict)
for k in kwargs:
if k in clashing_keywords:
kwargs['_'+k] = kwargs.pop(k)
return kwargs
。可以这样使用:
_
对我来说一直很好。希望这会有所帮助!