为什么不考虑级别如何评估python中的日志记录语句?
例如,在此代码中,我希望仅在使用“ -d”调用脚本时才打印“我已执行”语句,但始终会打印!这意味着日志记录语句可能会对在更高日志记录级别运行的代码产生意外影响。
#!/usr/bin/env python3
#import time
import argparse
import logging
logging.basicConfig(format='==> %(module)s, %(funcName)s %(message)s', level=logging.ERROR)
def logme():
#time.sleep(10)
print('I was executed ☠')
return 'loggging all the things...'
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("--debug", "-d",
action='store_true',
help="Debug mode (very verbose)",
)
args = parser.parse_args()
if args.debug:
logging.getLogger().setLevel(logging.DEBUG)
print('hello')
logging.debug('{}'.format(logme()))
print('bye')
这是日志记录模块中的错误吗?
答案 0 :(得分:3)
它归结为如何评估该语句。在
logging.debug('{}'.format(logme()))
首先评估参数,然后调用logger.debug
。因此,我们评估'{}'.format(logme())
,然后将结果传递给logging.debug
。有效
x = '{}'.format(logme())
logging.debug(x)
让我们对此进行测试:
def run_later(x):
print("function call")
def effect():
print("parameter evaluation")
run_later(effect())
>>> parameter evaluation
>>> function call
答案 1 :(得分:2)
这是日志记录模块中的错误吗?
不。首先,输出 不同(将您的代码复制到(?!\*)
中):
test.py
vs。
PS C:\Users\Matt> python test.py
hello
I was executed.
bye
第二,如果您在程序PS C:\Users\Matt> python test.py -d
hello
I was executed.
==> test, <module> loggging all the things...
bye
的任何位置调用logme()
,则会打印到屏幕上。这是因为"I was executed"
包含语句logme()
。此特定行为与print('I was executed ☠')
模块无关。它打印到屏幕上,因为您在执行操作时正在调用logging
函数:
logme
但是, logging 与 printing 不同,这就是为什么我们看到不同的输出的原因。调用logging.debug('{}'.format(logme()))
时将始终显示 ,但是请注意,当指定"I was executed"
标志时,"==> test, <module> loggging all the things..."
仅被记录 。碰巧的是,您在调用日志配置时将日志配置设置为 print (即-d
)。您可以登录文件,或执行其他类似的操作。
logging.basicConfig(format='==> %(module)s, %(funcName)s %(message)s', level=logging.ERROR
模块中使用格式字符串:此外,如果您想在记录器中使用格式字符串,则实际上是使用“%”样式格式设置,但是您不实际上想要提供格式字符串。而是以args形式提供格式。之所以这样做,是因为除非记录器在适当的级别被调用,否则格式替换不会发生。替换为格式字符串是一个相对昂贵的操作,尤其是(例如)您的一个调试语句处于循环中时。我的意思示例(将您的logging
语句替换为下面的语句)
logging.debug
那么我们有:
# logging.debug('{}'.format(logme()))
logging.debug('Some %s format %s string', 'first', 'second')
和:
PS C:\Users\Matt> python test.py
hello
bye
HTH。
答案 2 :(得分:0)
在日志记录功能内部做出了决定做某事的决定,这就是为什么必须至少输入logging.debug的原因。在此之前运行并评估您的logme函数,以便可以传递其结果,这就是为什么您看到“我已被执行?”的原因。
答案 3 :(得分:0)
其他答案告诉您为什么总是对logme()
求值,但是如果您确实希望避免这种情况,可以通过确保仅在结果转换为字符串的情况下才调用logme
:
#!/usr/bin/env python3
#import time
import argparse
import logging
logging.basicConfig(format='==> %(module)s, %(funcName)s %(message)s', level=logging.ERROR)
class LazyStr:
def __init__(self, fn, *args, **kw):
self.fn = fn
self.args = args
self.kw = kw
def __str__(self):
return str(self.fn(*self.args, **self.kw))
def logme(n, foo):
#time.sleep(10)
print('I was executed, n=%d, foo=%s' % (n, foo))
return 'loggging all the things...'
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("--debug", "-d",
action='store_true',
help="Debug mode (very verbose)",
)
args = parser.parse_args()
if args.debug:
logging.getLogger().setLevel(logging.DEBUG)
print('hello')
logging.debug('%s', LazyStr(logme, 42, foo='bar'))
print('bye')
将始终创建LazyStr
对象,但是仅在日志级别为logme()
时调用debug
函数。我还向logme
添加了一些参数,以说明如何传递参数。
输出:
$ ./t.py
hello
bye
$ ./t.py --debug
hello
I was executed, n=42, foo=bar
==> t, <module> loggging all the things...
bye