【问题标题】:Send subprocess.Popen stdout, stderr to logging module发送 subprocess.Popen 标准输出、标准错误到日志模块
【发布时间】:2016-05-31 01:25:36
【问题描述】:

我想使用 Python 的 subprocess.Popen 启动一个应用程序,并将应用程序 stdout 和 stderr 的输出以这样的方式发送到日志记录模块,每次应用程序向 stdout/stderr 发送某些内容/someline 时,它​​都会显示为logging.INFO("This is whats in stdout") .

由于应用程序是一个守护进程,它确实(并且应该)不会终止。

有没有一种简单的方法可以实现这一点,还是我必须使用单独的线程经常检查进程输出?

【问题讨论】:

    标签: python python-2.7 logging subprocess


    【解决方案1】:

    这是我从j-f-sebastian's answer 中提取的可重用类:

    import subprocess
    from threading import Thread
    
    class BackgroundPopen(subprocess.Popen):
        @staticmethod
        def prefix_handler(prefix, io):
            return lambda line: io.write(prefix + line)
    
        @staticmethod
        def _proxy_lines(pipe, handler):
            with pipe:
                for line in pipe:
                    handler(line)
    
        def __init__(self, out_handler, err_handler, *args, **kwargs):
            kwargs['stdout'] = subprocess.PIPE
            kwargs['stderr'] = subprocess.PIPE
            super(self.__class__, self).__init__(*args, **kwargs)
            Thread(target=self._proxy_lines, args=[self.stdout, out_handler]).start()
            Thread(target=self._proxy_lines, args=[self.stderr, err_handler]).start()
    

    【讨论】:

    • 在这个例子中,prefix_handler 是可能的out_handlererr_handler 的一个例子;它不会覆盖 subprocess.Popen 中的任何内容,可以安全地省略。
    【解决方案2】:

    线程是一种使用输出的简单且可移植的方式(未测试):

    #!/usr/bin/env python
    import logging
    from subprocess Popen, PIPE, STDOUT
    from threading import Thread
    
    def consume_lines(pipe, consume):
        with pipe:
            for line in iter(pipe.readline, b''): #NOTE: workaround read-ahead bug
                consume(line)
    
    logging.basicConfig(level=logging.INFO, format='%(asctime)s %(message)s')
    consume = lambda line: logging.info('this is whats in the output: %r', line)
    
    process = Popen(command, stdout=PIPE, stderr=STDOUT, bufsize=1)
    Thread(target=consume_lines, args=[process.stdout, consume]).start()
    process.wait()
    

    【讨论】:

      【解决方案3】:

      根据我的经验,单独的线程是要走的路。我的sarge 库就是这样做的——它在后台使用线程来捕获子进程的输出。事实上,我通常使用两个线程(一个用于stdout,一个用于stderr),除非我在subprocess.Popen 调用中合并两个输出流。

      【讨论】:

      • 这个答案最好有一个代码示例(我知道链接页面中有一个)。
      • 还有一种替代方法,使用非阻塞流并在等待子进程完成时在主线程中捕获它:*.com/a/9115366/582917 你认为这与线程的想法如何?
      • @CMCDragonkai 该示例使用select,因此它不适合Windows。