1. ホーム
  2. python

[解決済み] sys.stdoutをログファイルに複製する方法は?

2022-04-26 13:33:31

質問

Pythonアプリが多くのシステムコールを行っているときにロギングを行うための最良の方法は何でしょうか?

私のアプリには2つのモードがあります。 インタラクティブモードでは、システムコールからの出力を含め、すべての出力を画面とログファイルに出力したいと思います。 デーモン・モードでは、すべての出力はログに送られます。 デーモン・モードは os.dup2() . インタラクティブモードで、すべての出力をログに出力する方法は、システムコールの一つ一つを変更することなく、見つけることができません。


言い換えれば、私はpythonアプリによって生成されたすべての出力に対して、コマンドラインの'tee'の機能が欲しいのです。 システムコール出力を含む .

明確にするために

すべての出力をリダイレクトするために、私は次のようなことを行っています。

# open our log file
so = se = open("%s.log" % self.name, 'w', 0)

# re-open stdout without buffering
sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)

# redirect stdout and stderr to the log file opened above
os.dup2(so.fileno(), sys.stdout.fileno())
os.dup2(se.fileno(), sys.stderr.fileno())

これの良いところは、他のコードから特別なプリントを呼び出す必要がないことです。 このコードではいくつかのシェルコマンドも実行しているので、それらの出力も個別に処理する必要がないのは良いことです。

単純に、同じことをしたいのですが、ただし 複製 リダイレクトではなく

最初は、単純に反転させると dup2 が動作するはずです。 なぜ、うまくいかないのでしょうか? 以下は私のテストです。

import os, sys

### my broken solution:
so = se = open("a.log", 'w', 0)
sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)

os.dup2(sys.stdout.fileno(), so.fileno())
os.dup2(sys.stderr.fileno(), se.fileno())
###

print("foo bar")

os.spawnve("P_WAIT", "/bin/ls", ["/bin/ls"], {})
os.execve("/bin/ls", ["/bin/ls"], os.environ)

ファイル "a.log" は、画面に表示されたものと同じである必要があります。

解決するには?

コードから外部プロセスを生成することに抵抗がないのであれば、このように tee そのものです。 と全く同じことをするUnixのシステムコールを私は知りません。 tee が行う。

# Note this version was written circa Python 2.6, see below for
# an updated 3.3+-compatible version.
import subprocess, os, sys

# Unbuffer output (this ensures the output is in the correct order)
sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)

tee = subprocess.Popen(["tee", "log.txt"], stdin=subprocess.PIPE)
os.dup2(tee.stdin.fileno(), sys.stdout.fileno())
os.dup2(tee.stdin.fileno(), sys.stderr.fileno())

print "\nstdout"
print >>sys.stderr, "stderr"
os.spawnve("P_WAIT", "/bin/ls", ["/bin/ls"], {})
os.execve("/bin/ls", ["/bin/ls"], os.environ)

をエミュレートすることもできます。 tee を使用して マルチプロセシング パッケージ(または 処理 は、Python 2.5以前を使用している場合)。

更新情報

Python 3.3+対応版はこちらです。

import subprocess, os, sys

tee = subprocess.Popen(["tee", "log.txt"], stdin=subprocess.PIPE)
# Cause tee's stdin to get a copy of our stdin/stdout (as well as that
# of any child processes we spawn)
os.dup2(tee.stdin.fileno(), sys.stdout.fileno())
os.dup2(tee.stdin.fileno(), sys.stderr.fileno())

# The flush flag is needed to guarantee these lines are written before
# the two spawned /bin/ls processes emit any output
print("\nstdout", flush=True)
print("stderr", file=sys.stderr, flush=True)

# These child processes' stdin/stdout are 
os.spawnve("P_WAIT", "/bin/ls", ["/bin/ls"], {})
os.execve("/bin/ls", ["/bin/ls"], os.environ)