我编写了一些单元测试,用于分析使用标准python日志记录功能记录的数据。使用我在这里找到的一些想法:Capture stdout from a script in Python关于如何从stderr捕获数据,我已经提出了以下脚本,我将其简化为最低限度,以说明我遇到的问题。 (下面的循环模拟了可以从各种单元测试中调用此函数的事实)
import logging, sys
from StringIO import StringIO
def get_stderr():
saved_stderr = sys.stderr
stderr_string_io = StringIO()
sys.stderr = stderr_string_io
try:
logging.error("Foobar!!!")
finally:
# set the stdout and stderr back to their original values
sys.stderr = saved_stderr
err_output = stderr_string_io.getvalue()
return err_output
for x in [1, 2]:
err_output = get_stderr()
print "Run %d: %s" % (x, err_output)
如果运行脚本,它将提供以下输出,其中第二次循环迭代的日志记录输出完全丢失:
Run 1: ERROR:root:Foobar!!!
Run 2:
Process finished with exit code 0
虽然我希望它能提供以下输出:
Run 1: ERROR:root:Foobar!!!
Run 2: ERROR:root:Foobar!!!
Process finished with exit code 0
注意:在函数末尾执行stderr_string_io.close()
不起作用,因为脚本会在下次执行函数时抛出ValueError
。
为什么此代码的行为不符合预期,解决此问题的解决方案是什么?
答案 0 :(得分:1)
致电时
logging.error
它运行
def error(msg, *args, **kwargs):
if len(root.handlers) == 0:
basicConfig()
root.error(msg, *args, **kwargs)
由于一开始没有根处理程序,它运行basicConfig
而没有参数,这样做:
def basicConfig():
_acquireLock()
try:
if len(root.handlers) == 0:
h = StreamHandler(None)
handlers = [h]
dfs = None
style = '%'
fs = kwargs.get("format", _STYLES[style][1])
fmt = Formatter(fs, dfs, style)
for h in handlers:
if h.formatter is None:
h.setFormatter(fmt)
root.addHandler(h)
finally:
_releaseLock()
我删除了没有参数时无法运行的代码。
所以这已设置handlers = [StreamHandler(None)]
:
class StreamHandler(Handler):
def __init__(self, stream=None):
Handler.__init__(self)
if stream is None:
stream = sys.stderr
self.stream = stream
这意味着您将顶级记录器永久地附加到您调用它时stdout
的任何内容。
这会导致您的问题,因为您丢弃了该输出。这意味着输出将转到死StringIO对象,并丢失。
解决此问题的一种方法是在更新handlers
时通过stderr
并替换引用stderr
的任何内容:
import logging, sys
from StringIO import StringIO
def get_stderr():
saved_stderr = sys.stderr
stderr_string_io = StringIO()
for handler in logging.root.handlers:
if handler.stream is sys.stderr:
handler.stream = stderr_string_io
sys.stderr = stderr_string_io
try:
logging.error("Foobar!!!")
finally:
# set the stdout and stderr back to their original values
for handler in logging.root.handlers:
if handler.stream is sys.stderr:
handler.stream = saved_stderr
sys.stderr = saved_stderr
err_output = stderr_string_io.getvalue()
return err_output
for x in [1, 2]:
err_output = get_stderr()
print "Run %d: %s" % (x, err_output)
我不知道这会有多好用。它也不会捕获任何不是根记录器的记录器。就个人而言,按价值捕获sys.stdout
的想法是荒谬的,这似乎是不可避免的结果。