[python] sys.stdout을 로그 파일에 복제하는 방법?

편집 : 해결책이 없거나 아무도 모르는 비표준 작업을하고있는 것처럼 보이므로-질문을 수정하여 다음과 같이 질문 할 것입니다. 파이썬 앱에서 많은 시스템 호출?

내 앱에는 두 가지 모드가 있습니다. 대화 형 모드에서는 모든 시스템 호출의 출력을 포함하여 모든 출력이 화면과 로그 파일로 이동하기를 원합니다. 데몬 모드에서는 모든 출력이 로그로 이동합니다. 데몬 모드는을 사용하여 훌륭하게 작동합니다 os.dup2(). 모든 시스템 호출을 수정하지 않고 대화식 모드에서 로그에 모든 출력을 “티”하는 방법을 찾을 수 없습니다.


즉, 시스템 호출 출력을 포함 하여 파이썬 앱으로 생성 된 모든 출력에 대해 명령 줄 ‘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스스로 사용할 수 있습니다 . 나는 정확히 무엇을하는지 유닉스 시스템 호출을 모른다 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)


답변

나는 전에 이와 같은 문제가 있었고이 발췌 문장이 매우 유용하다는 것을 알았습니다.

class Tee(object):
    def __init__(self, name, mode):
        self.file = open(name, mode)
        self.stdout = sys.stdout
        sys.stdout = self
    def __del__(self):
        sys.stdout = self.stdout
        self.file.close()
    def write(self, data):
        self.file.write(data)
        self.stdout.write(data)
    def flush(self):
        self.file.flush()

에서 : http://mail.python.org/pipermail/python-list/2007-May/438106.html


답변

print문은 호출 write()이 sys.stdout에 할당 개체의 방법을.

한 번에 두 곳에서 쓸 수있는 작은 수업을 시작합니다 …

import sys

class Logger(object):
    def __init__(self):
        self.terminal = sys.stdout
        self.log = open("log.dat", "a")

    def write(self, message):
        self.terminal.write(message)
        self.log.write(message)

sys.stdout = Logger()

이제 print명령문이 화면에 표시되고 로그 파일에 추가됩니다.

# prints "1 2" to <stdout> AND log.dat
print "%d %d" % (1,2)

이것은 분명히 빠르고 더럽습니다. 몇 가지 참고 사항 :

  • 로그 파일 이름을 매개 변수화해야합니다.
  • <stdout>프로그램 기간 동안 로깅하지 않으면 sys.stdout을 되돌려 야 합니다.
  • 한 번에 여러 로그 파일에 쓰거나 다른 로그 수준 등을 처리하는 기능을 원할 수 있습니다.

이것들은 독자들을위한 연습으로 남겨 두는 것이 편할 정도로 간단합니다. 여기서 중요한 통찰력은에 print지정된 “파일과 유사한 객체”를 호출한다는 것 sys.stdout입니다.


답변

실제로 원하는 것은 logging표준 라이브러리의 모듈입니다. 로거를 작성하고 두 개의 핸들러를 첨부하십시오. 하나는 파일에 작성하고 다른 하나는 stdout 또는 stderr에 작성합니다.

자세한 내용 은 여러 대상로깅을 참조 하십시오


답변

여기 다른 솔루션보다 더 일반적인 또 다른 솔루션이 있습니다-출력 sys.stdout을 여러 파일과 유사한 객체로 나누는 것을 지원 합니다. 그 __stdout__자체가 포함되어 있을 필요는 없습니다 .

import sys

class multifile(object):
    def __init__(self, files):
        self._files = files
    def __getattr__(self, attr, *args):
        return self._wrap(attr, *args)
    def _wrap(self, attr, *args):
        def g(*a, **kw):
            for f in self._files:
                res = getattr(f, attr, *args)(*a, **kw)
            return res
        return g

# for a tee-like behavior, use like this:
sys.stdout = multifile([ sys.stdout, open('myfile.txt', 'w') ])

# all these forms work:
print 'abc'
print >>sys.stdout, 'line2'
sys.stdout.write('line3\n')

참고 : 이것은 개념 증명입니다. 여기서 구현 은 파일과 같은 객체 (예 :)의 메소드 만 래핑 하기 때문에 write멤버 / 속성 / setattr 등을 제외 하기 때문에 완전하지 않습니다 . 그러나 현재 대부분의 사람들에게는 충분할 것입니다.

내가 그것에 대해 좋아, 그 일반성 이외는 의미에서 어떤 직접 호출을하지 않는 깨끗한 지이며 write, flush, os.dup2, 등


답변

다른 곳에서 설명했듯이 가장 좋은 해결책은 로깅 모듈을 직접 사용하는 것입니다.

import logging

logging.basicConfig(level=logging.DEBUG, filename='mylog.log')
logging.info('this should to write to the log file')

그러나, 당신이 정말로 원하는 (희소 한) 경우가 있습니다 stdout을 리디렉션 경우는 드 ra니다. print를 사용하는 django의 runserver 명령을 확장 할 때이 상황이 발생했습니다 .django 소스를 해킹하고 싶지 않지만 파일로 이동하려면 print 문이 필요했습니다.

이것은 로깅 모듈을 사용하여 stdout 및 stderr을 쉘에서 다른 방향으로 리디렉션하는 방법입니다.

import logging, sys

class LogFile(object):
    """File-like object to log text using the `logging` module."""

    def __init__(self, name=None):
        self.logger = logging.getLogger(name)

    def write(self, msg, level=logging.INFO):
        self.logger.log(level, msg)

    def flush(self):
        for handler in self.logger.handlers:
            handler.flush()

logging.basicConfig(level=logging.DEBUG, filename='mylog.log')

# Redirect stdout and stderr
sys.stdout = LogFile('stdout')
sys.stderr = LogFile('stderr')

print 'this should to write to the log file'

로깅 모듈을 직접 사용할 수없는 경우에만이 LogFile 구현을 사용해야합니다.


답변

필자는 tee()대부분의 경우 작동하는 Python 구현을 작성 했으며 Windows에서도 작동합니다.

https://github.com/pycontribs/tendo

또한 logging원하는 경우 Python의 모듈 과 함께 사용할 수 있습니다 .