【问题标题】:stdout printed in stderr file while using logger使用记录器时在 stderr 文件中打印的标准输出
【发布时间】:2013-06-17 00:41:05
【问题描述】:

我正在使用 logger 打印出 stdout 和 stderr 来记录文件。我已经这样做了:

def log(process):

    logger = logging.getLogger('logging_errors')
    if not len(logger.handlers):
        logger.setLevel(logging.DEBUG)
        formatter = logging.Formatter('%(levelname)s %(asctime)s %(module)s %(message)s')

        handler_stderr = logging.FileHandler('stderr.log')
        handler_stderr.setLevel(logging.WARNING)
        handler_stderr.setFormatter(formatter)
        logger.addHandler(handler_stderr)

        handler_stdout = logging.FileHandler('stdout.log')
        handler_stdout.setLevel(logging.DEBUG)
        handler_stdout.setFormatter(formatter)
        logger.addHandler(handler_stdout)
    return logger.error(process.stderr.read())
    return logger.info(process.stdout.read())

一个进程被传递给这个函数,它可以是这样的:

proc = subprocess.Popen(['FastTree -nt test.fasta'], stdin = None, stdout = subprocess.PIPE, stderr = subprocess.PIPE, shell=True) 
proc.wait()
log(proc)

我遇到的问题是 stdout 没有打印到 stdout.log 文件中,我在 stdout.log 文件中得到了 stderr。 stderr.log 文件是正确的。有没有这方面的指点?

【问题讨论】:

    标签: python logging stdout stderr


    【解决方案1】:

    你有

    return logger.error(process.stderr.read())
    return logger.info(process.stdout.read())
    

    第二个return 语句没有被执行,所以进程'stdout 永远不会被记录。只需从这些语句中删除return,您应该会发现输出出现在stdout.log 中。

    您在stdout.log 中获得stderr 输出,因为所有记录的事件都传递给了两个处理程序。

    【讨论】:

    • 我这样做了,我确实得到了正确的标准输出,但它只运行一个进程。我添加了一个额外的过程,但它忽略了它。
    【解决方案2】:

    您可以设置两个记录器,一个用于 stdout,一个用于 stderr,但是您必须编写

    info_logger.info(out)
    error_logger.error(err)
    

    这是重复的,因为当它们都包含基本相同的信息时,您必须同时选择适当的记录器和适当的级别。

    所以,我们可以稍微花点心思,使用robert's LevelFilter 创建一个带有两个过滤处理程序的记录器。每个处理程序都有一个过滤器,只接受那些级别小于或等于end_level 的 LogRecords:

    handler.addFilter(LevelFilter(end_level))
    

    同时,声明

    handler.setLevel(begin_level)
    

    导致handler 只接受级别大于begin_level 的日志记录。结合起来,我们有一个处理程序,它只接受级别在begin_levelend_level 之间的那些 LogRecords。因此,您可以让一个处理程序处理 DEBUG 和 INFO LogRecords,并让第二个处理程序处理 WARNING、ERROR 和 CRITICAL LogRecords。

    把它们放在一起,你可以使用这样的东西:

    import logging
    import subprocess
    
    class LevelFilter(logging.Filter):
        """
        https://stackoverflow.com/a/7447596/190597 (robert)
        """
        def __init__(self, level):
            self.level = level
    
        def filter(self, record):
            return record.levelno <= self.level
    
    def splitlevel_logger(name, outpath='stderr.log', errpath='stdout.log',
                          fmt='%(levelname)s %(asctime)s %(module)s %(message)s'):
        formatter = logging.Formatter(fmt)
        logger = logging.getLogger()
        logger.setLevel(logging.DEBUG)
        for filename, begin_level, end_level in (
                (outpath, logging.WARNING, logging.CRITICAL),
                (errpath, logging.DEBUG, logging.INFO)):
            handler = logging.FileHandler(filename, 'w')
            handler.setLevel(begin_level)
            handler.addFilter(LevelFilter(end_level))
            handler.setFormatter(formatter)
            logger.addHandler(handler)
        return logger
    
    def log(out='', err=''):
        if out:
            logger.info(out)
        if err:
            logger.error(err)
    
    logger = splitlevel_logger(__name__)
    cmd = 'FastTree -nt test.fasta'
    proc = subprocess.Popen(cmd,
                            stdout = subprocess.PIPE,
                            stderr = subprocess.PIPE, shell=True) 
    log(*proc.communicate())
    

    顺便提一下关于subprocess.Popen.wait的警告:

    警告

    [Popen.wait] 在使用 stdout=PIPE 和/或 stderr=PIPE 和 子进程向管道生成足够的输出,使其阻塞 等待操作系统管道缓冲区接受更多数据。使用通信() 避免这种情况。

    因此,在上述情况下使用Popen.wait 可能会出现问题。使用Popen.communicate 来避免这个问题。 或者,如果您想在子进程输出时读取并记录输出(而不是仅在子进程完成后接收)use threadingselect.select

    【讨论】:

      猜你喜欢
      • 2012-11-23
      • 2013-07-25
      • 1970-01-01
      • 1970-01-01
      • 2020-09-03
      • 2022-01-07
      • 2015-08-31
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多