简体   繁体   English

如何临时重定向Python中的日志记录输出?

[英]How can I temporarily redirect the output of logging in Python?

There's already a question that answers how to do this regarding sys.stdout and sys.stderr here: https://stackoverflow.com/a/14197079/198348 已经有一个问题可以解答如何在sys.stdoutsys.stderr这里解决这个问题: httpssys.stdout

But that doesn't work everywhere. 但这并不适用于所有地方。 The logging module seems to output to sys.stdout and sys.stderr , but I can't capture it with the context manager above. 日志记录模块似乎输出到sys.stdoutsys.stderr ,但我无法使用上面的上下文管理器捕获它。

In the following example code, I'm trying to capture all output inside the context manager, failing to do so for the logger statements: 在下面的示例代码中,我试图捕获上下文管理器中的所有输出,但是没有为记录器语句执行此操作:

from __future__ import print_function
import contextlib
import sys
import logging
from StringIO import StringIO

# taken from https://stackoverflow.com/a/14197079/198348
@contextlib.contextmanager
def stdout_redirect(where):
    prev_stdout = sys.stdout
    prev_stderr = sys.stderr
    prev_stdout.flush()
    sys.stdout = where
    sys.stderr = where
    try:
        yield where
    finally:
        where.flush()
        sys.stdout = prev_stdout
        sys.stderr = prev_stderr

logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger()

print("\t\tOUTSIDE: stdout", file=sys.stdout)
print("\t\tOUTSIDE: stderr", file=sys.stderr)
logger.info("\tOUTSIDE: info")
logger.debug("\tOUTSIDE: debug")
logger.warn("\tOUTSIDE: warn")
logger.error("\tOUTSIDE: error")
logger.critical("\tOUTSIDE: critical")

print("=============== DIVIDER ================")

s = ""
with stdout_redirect(StringIO()) as new_stdout:
    print("\t\tINSIDE: stdout", file=sys.stdout)
    print("\t\tINSIDE: stderr", file=sys.stderr)
    logger.info("\tINSIDE: info")
    logger.debug("\tINSIDE: debug")
    logger.warn("\tINSIDE: warn")
    logger.error("\tINSIDE: error")
    logger.critical("\tINSIDE: critical")


print("=============== DIVIDER ===============")
print(new_stdout.getvalue())

print("=============== LOGGING ===============")

print(logger.handlers)
print(logger.root.handlers)

How can I temporarily redirect the output of the logger(s) that spit out to stdout and capture them? 如何暂时将吐出的记录器的输出重定向到stdout并捕获它们? I took a look at logging/ init .py , but it doesn't immediately tell me what I need to do. 我看了一下logging / init .py ,但它没有立即告诉我我需要做什么。

My motivation for doing this is that I want to equip a crufty big codebase with tests, each of which captures the spurious amounts of logging output that each test invokes. 我这样做的动机是,我想为一个狡猾的大代码库配备测试,每个测试都会捕获每个测试调用的虚假记录输出量。 I can capture external programs, but I can't seem to capture the tests that I run inside nose . 我可以捕获外部程序,但我似乎无法捕获我在鼻子里面运行的测试。

Rewriting the verbose parts isn't an option right now, but is definitely a goal for further down the road. 重写冗长的部分现在不是一个选项,但绝对是未来的目标。

Edit, regarding ubuntu 编辑,关于ubuntu

Here's what I've tried running with nosetests: 这是我尝试使用nosetests运行的:

from __future__ import print_function
import sys

def test_funky_shurane():
    import logging
    logging.basicConfig(level=logging.DEBUG)
    logging.info("===== shurane info")
    logging.warn("===== shurane warn")
    logging.error("===== shurane error")
    logging.critical("===== shurane critical")
    print("===== shurane stdout", file=sys.stdout)
    print("===== shurane stderr", file=sys.stderr)
    assert True

And then running the above with: 然后运行上面的:

nosetests test_logging.py
nosetests --nocapture test_logging.py

the logging.basicConfig() is a convenience that sets up some logger handling in a very simple way. logging.basicConfig()是一种方便,以非常简单的方式设置一些记录器处理。 If you need a little more than that, you shouldn't use basicConfig() . 如果你需要更多,你不应该使用basicConfig() That's not a big deal, because it doesn't do a whole lot. 这不是什么大问题,因为它并没有做很多事情。 What we need is to configure logging for both streams; 我们需要的是配置两个流的日志记录;

import logging, sys
fmt = logging.Formatter(BASIC_FORMAT)

hdlr_stderr = logging.StreamHandler(sys.stderr)
hdlr_stderr.setFormatter(fmt)
hdlr_stdout = logging.StreamHandler(sys.stdout)
hdlr_stdout.setFormatter(fmt)
root.addHandler(hdlr_stderr)
root.addHandler(hdlr_stdout)
root.setLevel(logging.DEBUG)

By default, loggers log all messages that they receive; 默认情况下,记录器记录他们收到的所有消息; but initially, we don't want to log any messages to sys.stdout : 但最初,我们不想将任何消息记录到sys.stdout

hdlr_stdout.level = float('inf')  # larger than any log level; nothing gets logged

Then, your context manager might look a bit like: 然后,您的上下文管理器可能看起来像:

@contextlib.contextmanager
def redirect_stderr_logging(where):
    hdlr_stderr.level = float('inf')
    hdlr_stdout.level = logging.NOTSET
    try:
        yield where
    finally:
        hdlr_stderr.level = logging.NOTSET
        hdlr_stdout.level = float('inf')

The redirect does not work because the logger that you set up initially has a pointer directly to stdout. 重定向不起作用,因为您最初设置的记录器有一个直接指向stdout的指针。 See below. 见下文。

logging_test.py logging_test.py

import logging
import sys

SIMPLE_LOG_FORMAT = '[%(asctime)s] {%(filename)s:%(lineno)d} %(levelname)s - %(message)s'
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
formatter = logging.Formatter(SIMPLE_LOG_FORMAT)
console_handler = logging.StreamHandler(sys.stdout)
console_handler.setFormatter(formatter)
logger.addHandler(console_handler)

logger.info("Message before redirecting stdout and stderr")
# Checking what logger is writing to
logger.info('Before Redirection. logger writing to {} '.format(logger.handlers[0].stream))

log_file = open('log_file.log', 'w')
sys.stdout = log_file
sys.stderr = log_file
# Checking what logger is writing to
logger.info('After Redirection. logger writing to {} '.format(logger.handlers[0].stream))
logger.info("Message after redirecting stdout and stderr")

log_file.close()

Output: 输出:

[2018-06-01 16:27:10,670] {logging_test.py:12} INFO - Message before redirecting stdout and stderr
[2018-06-01 16:27:10,670] {logging_test.py:14} INFO - Before Redirection. logger writing to <open file '<stdout>', mode 'w' at 0x10cd74150> 
[2018-06-01 16:27:10,670] {logging_test.py:20} INFO - After Redirection. logger writing to <open file '<stdout>', mode 'w' at 0x10cd74150> 
[2018-06-01 16:27:10,670] {logging_test.py:21} INFO - Message after redirecting stdout and stderr

As you can see on the second and third lines of the output that logger still refers to directly. 正如您在输出的第二行和第三行看到的那样,记录器仍然直接引用。

One of the ways to fix this is to do something like this 解决这个问题的方法之一是做这样的事情

logger.handlers[0].stream = open('log_file.log', 'w')

Note that if you are using multiple threads a change like this in one will cause output from other threads to also start redirecting output. 请注意,如果您使用多个线程,则此类更改将导致其他线程的输出也开始重定向输出。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM