2012-02-02 13 views
2

私はPythonのプロセスを開始し、親プロセスのログオブジェクトにサブプロセスのエラーメッセージを記録したいと思います。ログ・ストリームを1つのファイルに統合するのが理想的です。私は何とかロギングクラスの出力ストリームにアクセスできますか?私が知っている1つの解決策は、ロギングにprocログを使用することです。下の答えで説明したように、私はproc.stdinとstderrから読むことができましたが、重複したロギングヘッダーがあります。私は、ファイル記述子をロギングクラスの基礎となるサブプロセスに直接渡す方法があるのだろうか?Pythonのログとサブプロセスの出力とエラーストリーム

logging.basicConfig(filename="test.log",level=logging.DEBUG) 
logging.info("Started") 
procLog = open(os.path.expanduser("subproc.log"), 'w') 
proc = subprocess.Popen(cmdStr, shell=True, stderr=procLog, stdout=procLog) 
proc.wait() 
procLog.flush() 

答えて

3

Adam Rosenfield's codeに基づいて、あなたは

  1. 使用select.select
  2. 繰り返した後、その出力を proc.stdoutproc.stderrから読み取るべき出力があるまで、
  3. 読み込みをブロックし、ログに記録することができプロセスが完了するまで

以下は、/tmp/test.logに書き込んで、ls -laR /tmpコマンドを実行することに注意してください。ニーズに合わせて変更してください。

(PS:通常/ tmpがそうls -laR /tmpがstdoutとstderrの両方に出力を生成し実行する、通常のユーザが読み取ることができないディレクトリが含まれ、それらが生成されるように以下のコードが正しく、これら2つのストリームをインターリーブする。)

import logging 
import subprocess 
import shlex 
import select 
import fcntl 
import os 
import errno 
import contextlib 

logger = logging.getLogger(__name__) 

def make_async(fd): 
    '''add the O_NONBLOCK flag to a file descriptor''' 
    fcntl.fcntl(fd, fcntl.F_SETFL, fcntl.fcntl(fd, fcntl.F_GETFL) | os.O_NONBLOCK) 

def read_async(fd): 
    '''read some data from a file descriptor, ignoring EAGAIN errors''' 
    try: 
     return fd.read() 
    except IOError, e: 
     if e.errno != errno.EAGAIN: 
      raise e 
     else: 
      return '' 

def log_fds(fds): 
    for fd in fds: 
     out = read_async(fd) 
     if out: 
      logger.info(out) 

@contextlib.contextmanager 
def plain_logger(): 
    root = logging.getLogger()  
    hdlr = root.handlers[0] 
    formatter_orig = hdlr.formatter 
    hdlr.setFormatter(logging.Formatter('%(message)s')) 
    yield 
    hdlr.setFormatter(formatter_orig) 

def main(): 
    # fmt = '%(name)-12s: %(levelname)-8s %(message)s' 
    logging.basicConfig(filename = '/tmp/test.log', mode = 'w', 
         level = logging.DEBUG) 

    logger.info("Started") 
    cmdStr = 'ls -laR /tmp' 

    with plain_logger(): 
     proc = subprocess.Popen(shlex.split(cmdStr), 
           stdout = subprocess.PIPE, stderr = subprocess.PIPE) 
     # without `make_async`, `fd.read` in `read_async` blocks. 
     make_async(proc.stdout) 
     make_async(proc.stderr) 
     while True: 
      # Wait for data to become available 
      rlist, wlist, xlist = select.select([proc.stdout, proc.stderr], [], []) 
      log_fds(rlist) 
      if proc.poll() is not None: 
       # Corner case: check if more output was created 
       # between the last call to read_async and now     
       log_fds([proc.stdout, proc.stderr])     
       break 

    logger.info("Done") 

if __name__ == '__main__': 
    main() 

編集:

あなたはlogfile = open('/tmp/test.log', 'a')からstdoutstderrをリダイレクトすることができます。 しかし、これを実行するのが少し難しいのは、/tmp/test.logにも書き込んでいるすべてのロガーハンドラは、サブプロセスが何を書き込んでいるのか分からないため、ログファイルが文字化けする可能性があります。

サブプロセスが業務を行っているときにロギングコールを行わない場合、唯一の問題は、サブプロセスの終了後にロガーハンドラのファイル内の位置が間違っていることです。これは、

handler.stream.seek(0, 2) 

を呼び出すことで修正できるため、ハンドラはファイルの最後に書き出しを再開します。


import logging 
import subprocess 
import contextlib 
import shlex 

logger = logging.getLogger(__name__) 

@contextlib.contextmanager 
def suspended_logger(): 
    root = logging.getLogger()  
    handler = root.handlers[0] 
    yield 
    handler.stream.seek(0, 2) 

def main(): 
    logging.basicConfig(filename = '/tmp/test.log', filemode = 'w', 
         level = logging.DEBUG) 

    logger.info("Started") 
    with suspended_logger(): 
     cmdStr = 'test2.py 1>>/tmp/test.log 2>&1' 
     logfile = open('/tmp/test.log', 'a') 
     proc = subprocess.Popen(shlex.split(cmdStr), 
           stdout = logfile, 
           stderr = logfile) 
     proc.communicate() 
    logger.info("Done") 

if __name__ == '__main__': 
    main() 
+0

これは動作しますが、私は、ログファイルに追加しようとしている出力は、別のプログラムからロガーストリームです。だから、もし私がこれをしたら、私はラインごとに2つのタイムスタンプを得るでしょう(これがはっきりしないかどうか私に教えてください)。では、ロガープレフィックスをバイパスして、不必要なロギングファイル記述子に直接書き込む方法はありますか? – fodon

+0

一時的にロギング形式を '%(message)s 'に変更することができます。上記のようにいくつかのコードを追加しました。 – unutbu

+1

うん、はい、それはうまくいきますが、プロセスにディスクリプタを渡してファイルできればそれほどエレガントではありませんか?本当にそれはできませんか? – fodon

関連する問題