0

我使用记录器打印出标准输出和标准错误来记录文件。我已经这样做了:

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 文件是正确的。对此有任何指示吗?

4

2 回答 2

2

你有

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

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

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

于 2013-06-20T09:00:42.363 回答
0

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

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

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

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

handler.addFilter(LevelFilter(end_level))

同时,声明

handler.setLevel(begin_level)

导致handler仅接受级别大于 的那些 LogRecords begin_level。结合起来,我们有一个处理程序,它只接受那些级别在begin_level和之间的 LogRecords end_level。因此,您可以让一个处理程序处理 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 时会死锁,并且子进程会向管道生成足够的输出,从而阻塞等待 OS 管道缓冲区接受更多数据。使用communicate() 来避免这种情况。

Popen.wait因此,在上述情况下使用它可能是错误的。用于Popen.communicate避免此问题。或者,如果您想在子进程输出时读取和记录输出(而不是仅在子进程完成后接收它),请使用 threadingselect.select

于 2013-06-25T12:41:08.250 回答