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


149

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

내 앱에는 두 가지 모드가 있습니다. 대화 형 모드에서는 모든 시스템 호출의 출력을 포함하여 모든 출력이 화면과 로그 파일로 이동하기를 원합니다. 데몬 모드에서는 모든 출력이 로그로 이동합니다. 데몬 모드는을 사용하여 훌륭하게 작동합니다 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"파일은 화면에 표시된 것과 동일해야합니다.


맨 페이지 ( manpagez.com/man/2/dup2 )를 보면 dup2의 두 번째 인수는 항상 닫힙니다 (이미 열려있는 경우). 따라서 "깨진 솔루션"에서는 닫히고 파일 이름을 sys.stdout에 다시 할당합니다.
Jacob Gabrielson 2016 년

1
다시 : 당신의 편집 : 이것은 드문 일이 아닙니다, 나는 다른 언어에서 몇 번 비슷한 작업을 수행했습니다. Unix는 동일한 파일 핸들에 대해 여러 개의 "별칭"을 허용하지만 파일 핸들을 "분할"하지는 않습니다 (여러 다른 파일에 복사). 따라서 "tee"를 직접 구현해야합니다 (또는 "tee"만 사용하십시오).
Jacob Gabrielson

JohnT 답변이 실제로 허용되는 답변보다 낫다고 생각합니다. 허용 된 답변을 변경하고 싶을 수도 있습니다.
Phong

"저는 비표준적인 일을하고 있습니다"– 당신은 정말로, 사람들은 단지 로그를 stderr에 보내고 커맨드 라인에서 처리합니다.
khachik

답변:


55

코드에서 외부 프로세스를 생성하는 것이 편하기 때문에 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)

28
글쎄,이 답변은 효과가 있으므로 받아 들일 것입니다. 아직도, 그것은 나를 만드는 기분이 더러운.
drue

2
방금 모든 플랫폼에서 실행할 수 있고 다른 로깅 구성에서 사용할 수있는 py (py2 / 3 호환)의 순수한 파이썬 구현을 게시했습니다. stackoverflow.com/questions/616645/…
소린

8
파이썬이 내 컴퓨터 중 하나에서 실행되고 솔루션이 작동하지 않으면 파이썬 솔루션이 아닙니다. 그로 인해 공감.
anatoly techtonik

2
에 따르면 이 게시물 선이 sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)더 이상 파이썬 3.3 이후 작업 (3116 PEP 참조)
켄 마이어스

1
"sys : 1 : ResourceWarning : 닫히지 않은 파일 <_io.BufferedWriter name = 5>"오류가 발생 tee.stdin.close()하여 프로그램 끝에 추가해야 했습니다. 또한 "ResourceWarning : 하위 프로세스 1842가 여전히 실행 중입니다"라는 메시지 sys.stdout.close(); sys.stderr.close()가 표시되며 프로그램 끝에 추가하면 문제 가 해결됩니다.
matthieu

136

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

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


7
Tee 객체를 삭제하여 로깅을 종료 할 수 있도록 sys.stdout 재 할당을 내부적으로 처리하는 +1
Ben Blank

12
나는 그것에 플러시를 추가 할 것입니다. 예 : 'self.file.flush ()'
Luke Stanley

4
로깅 모듈에 동의하지 않습니다. 약간의 fiddling에 우수합니다. 로깅이 너무 큽니다.
Kobor42

4
후속 답변 의 수정 된 버전 은 답변의 링크 된 토론에 유의하십시오 .
martineau

4
작동하지 않습니다. __del__실행이 끝날 때까지 호출되지 않습니다. 참조 stackoverflow.com/questions/6104535/...
NUX

77

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입니다.


정확히 내가 게시하려고하는 것은 거의. 자체 인수가없는 쓰기로 문제를 해결하면 +1 또한 작성하려는 파일을 전달하는 것이 더 좋은 디자인입니다. 지옥, stdout을 전달하는 것이 더 좋은 디자인 일 수도 있습니다.
Devin Jeanpierre

@Devin, 예, 빠르고 더러 웠습니다. 가능한 초기 개선 사항에 대해 몇 가지 메모를 드리겠습니다.
삼부작

7
이 답변을 너무 빨리 선택했습니다. "인쇄"에는 효과적이지만 외부 명령 출력에는 그다지 좋지 않습니다.
drue

2
로거 클래스는 또한 "def flush () : self.terminal.flush (); self.log.flush ()"와 같은 flush () 메소드를 정의해야합니다.
blokeley

5
당신은 말합니다 The print statement will call the write() method of any object you assign to sys.stdout. 그리고 사용하지 않는 stdout에 데이터를 보내는 다른 함수는 어떻습니까 print? 예를 들어 subprocess.call출력을 사용하여 프로세스를 만들면 콘솔로 이동하지만 log.dat파일로 보내지 않습니다 ... 고칠 수있는 방법이 있습니까?
jpo38

64

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

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


9
로깅 모듈은 예외 및 기타 중요한 출력을 stdout에 기록하지 않으므로 빌드 서버의 로그를 분석 할 때 유용 할 수 있습니다 (예 :).
anatoly techtonik

2
logging모듈은 다음과 같은 시스템 호출의 출력을 리디렉션하지 않습니다.os.write(1, b'stdout')
jfs

17

여기 다른 솔루션보다 더 일반적인 또 다른 솔루션이 있습니다-출력 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, 등


3
나는 파일이 아닌 * 파일 을 초기화 해야하지만 그렇지 않으면 그렇지 않습니다. 다른 솔루션은 다른 문제를 해결하지 않고 "티"기능을 분리하지 않습니다. 출력하는 모든 것에 접두사를 넣으려면이 클래스를 접두사 작성기 클래스로 래핑 할 수 있습니다. (하나의 스트림에 접두사를 붙이려면 스트림을 감싸서이 클래스에 넘겨주십시오.) 또한 multifile ([])은 모든 것을 무시하는 파일을 만드는 장점이 있습니다 (예 : open ( '/ dev /없는')).
Ben

_wrap여기에 전혀 없었 습니까? 거기에 코드를 복사 할 수 없으며 __getattr__동일하게 작동합니까?
timotree

@Ben은 실제로 메소드 중 하나를 호출 할 때마다 multifile([])파일을 생성 UnboundLocalError합니다. ( res지정되지 않은 채 반환 됨)
timotree

13

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

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 구현을 사용해야합니다.


11

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

https://github.com/pycontribs/tendo

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


흠-그 링크는 더 이상 작동하지 않습니다-다른 곳에서 찾을 수 있습니까?
Danny Staple

1
와우, 특히 Windows 콘솔 문화가 얼마나 번거롭지 만 작동하도록 포기하지 않았다면 패키지가 흔들립니다!
n611x007

8

(아, 질문을 다시 읽고 이것이 적용되지 않는지 확인하십시오.)

다음은 파이썬 로깅 모듈을 사용하는 샘플 프로그램입니다 . 이 로깅 모듈은 2.3 이후 모든 버전에서 사용되었습니다. 이 샘플에서는 로깅을 명령 줄 옵션으로 구성 할 수 있습니다.

완전 모드에서는 파일에만 기록되고, 정상 모드에서는 파일과 콘솔 모두에 기록됩니다.

import os
import sys
import logging
from optparse import OptionParser

def initialize_logging(options):
    """ Log information based upon users options"""

    logger = logging.getLogger('project')
    formatter = logging.Formatter('%(asctime)s %(levelname)s\t%(message)s')
    level = logging.__dict__.get(options.loglevel.upper(),logging.DEBUG)
    logger.setLevel(level)

    # Output logging information to screen
    if not options.quiet:
        hdlr = logging.StreamHandler(sys.stderr)
        hdlr.setFormatter(formatter)
        logger.addHandler(hdlr)

    # Output logging information to file
    logfile = os.path.join(options.logdir, "project.log")
    if options.clean and os.path.isfile(logfile):
        os.remove(logfile)
    hdlr2 = logging.FileHandler(logfile)
    hdlr2.setFormatter(formatter)
    logger.addHandler(hdlr2)

    return logger

def main(argv=None):
    if argv is None:
        argv = sys.argv[1:]

    # Setup command line options
    parser = OptionParser("usage: %prog [options]")
    parser.add_option("-l", "--logdir", dest="logdir", default=".", help="log DIRECTORY (default ./)")
    parser.add_option("-v", "--loglevel", dest="loglevel", default="debug", help="logging level (debug, info, error)")
    parser.add_option("-q", "--quiet", action="store_true", dest="quiet", help="do not log to console")
    parser.add_option("-c", "--clean", dest="clean", action="store_true", default=False, help="remove old log file")

    # Process command line options
    (options, args) = parser.parse_args(argv)

    # Setup logger format and output locations
    logger = initialize_logging(options)

    # Examples
    logger.error("This is an error message.")
    logger.info("This is an info message.")
    logger.debug("This is a debug message.")

if __name__ == "__main__":
    sys.exit(main())

좋은 대답입니다. 콘솔에 로깅을 복제하는 몇 가지 복잡한 방법을 보았지만 stderr로 StreamHandler를 만드는 것이 내가 찾은 답이었습니다.
meatvest

코드는 질문에 대답하지 않는 것이 좋습니다. 이것은 로그를 파일과 stderr에 출력합니다. 원래 질문은 stderr을 로그 파일에 복제하도록 요청했습니다.
emem

8

John T 답변을 완료하려면 https://stackoverflow.com/a/616686/395687

나는 추가 __enter____exit__방법은와 컨텍스트 매니저로 사용 with이 코드를 제공 키워드

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 __enter__(self):
        pass

    def __exit__(self, _type, _value, _traceback):
        pass

그런 다음으로 사용할 수 있습니다

with Tee('outfile.log', 'w'):
    print('I am written to both stdout and outfile.log')

1
__del__기능을 다음 위치로 옮길 것입니다__exit__
vontrapp

1
실제로, 나는 사용하는 __del__것이 나쁜 생각 이라고 생각합니다. 에서 호출되는 'close'함수로 이동해야합니다 __exit__.
cladmi

7

나는이 질문에 반복적으로 대답한다는 것을 알고 있지만,이를 위해 John T의 답변 에서 주요 답변을 가져 와서 수정 된 제안이 포함 된 수정 된 링크 된 버전을 따르도록 수정했습니다. 또한 with 문과 함께 사용하기 위해 cladmi의 답변에 언급 된대로 enter 및 exit를 추가했습니다 . 또한 설명서 에는 파일을 플러시하는 내용이 언급되어 os.fsync()있으므로 추가했습니다. 나는 당신이 정말로 그것을 필요로하는지 모르겠지만 거기에 있습니다.

import sys, os

class Logger(object):
    "Lumberjack class - duplicates sys.stdout to a log file and it's okay"
    #source: https://stackoverflow.com/q/616645
    def __init__(self, filename="Red.Wood", mode="a", buff=0):
        self.stdout = sys.stdout
        self.file = open(filename, mode, buff)
        sys.stdout = self

    def __del__(self):
        self.close()

    def __enter__(self):
        pass

    def __exit__(self, *args):
        self.close()

    def write(self, message):
        self.stdout.write(message)
        self.file.write(message)

    def flush(self):
        self.stdout.flush()
        self.file.flush()
        os.fsync(self.file.fileno())

    def close(self):
        if self.stdout != None:
            sys.stdout = self.stdout
            self.stdout = None

        if self.file != None:
            self.file.close()
            self.file = None

그런 다음 사용할 수 있습니다

with Logger('My_best_girlie_by_my.side'):
    print("we'd sing sing sing")

또는

Log=Logger('Sleeps_all.night')
print('works all day')
Log.close()

많은 Thnaks @Status 당신은 내 질문을 해결했습니다 ( stackoverflow.com/questions/39143417/… ). 귀하의 솔루션에 대한 링크를 드리겠습니다.
Mohammad ElNesr

1
@MohammadElNesr with 문과 함께 사용될 때 코드에 문제가 있음을 깨달았습니다. 나는 그것을 고쳤으며 이제 with 블록의 끝에서 올바르게 닫힙니다.
상태

1
이 기능은 저에게 mode="ab"writeself.file.write(message.encode("utf-8"))
효과적이었습니다.

4

로깅 모듈을 사용하는 다른 솔루션 :

import logging
import sys

log = logging.getLogger('stdxxx')

class StreamLogger(object):

    def __init__(self, stream, prefix=''):
        self.stream = stream
        self.prefix = prefix
        self.data = ''

    def write(self, data):
        self.stream.write(data)
        self.stream.flush()

        self.data += data
        tmp = str(self.data)
        if '\x0a' in tmp or '\x0d' in tmp:
            tmp = tmp.rstrip('\x0a\x0d')
            log.info('%s%s' % (self.prefix, tmp))
            self.data = ''


logging.basicConfig(level=logging.INFO,
                    filename='text.log',
                    filemode='a')

sys.stdout = StreamLogger(sys.stdout, '[stdout] ')

print 'test for stdout'

3

위의 답변 중 실제로 발생한 문제에 대한 답변은 없습니다. 나는 이것이 오래된 스레드라는 것을 알고 있지만,이 문제는 모든 사람들이 만드는 것보다 훨씬 간단하다고 생각합니다.

class tee_err(object):

 def __init__(self):
    self.errout = sys.stderr

    sys.stderr = self

    self.log = 'logfile.log'
    log = open(self.log,'w')
    log.close()

 def write(self, line):

    log = open(self.log,'a')
    log.write(line)
    log.close()   

    self.errout.write(line)

이제 이것은 일반적인 sys.stderr 핸들러와 파일에 모든 것을 반복합니다. 에 tee_out대한 다른 클래스 를 만듭니다 sys.stdout.


2
이보다 2 년 전에 유사한 더 나은 답변이 게시되었습니다 : stackoverflow.com/a/616686 . 귀하의 방법은 매우 비쌉니다. 호출 할 tee=tee_err();tee.write('');tee.write('');...때마다 파일마다 파일을 열거 나 닫습니다 write. 이 방법에 대한 인수는 stackoverflow.com/q/4867468stackoverflow.com/q/164053 을 참조하십시오 .
Rob W

3

@John T 's answer 아래의 주석에서 @ user5359531의 요청에 따라 해당 답변 의 수정 된 버전의 링크 된 토론에 대한 참조 게시물의 사본이 있습니다.

Issue of redirecting the stdout to both file and screen
Gabriel Genellina gagsl-py2 at yahoo.com.ar
Mon May 28 12:45:51 CEST 2007

    Previous message: Issue of redirecting the stdout to both file and screen
    Next message: Formal interfaces with Python
    Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]

En Mon, 28 May 2007 06:17:39 -0300, 人言落日是天涯,望极天涯不见家
<kelvin.you at gmail.com> escribió:

> I wanna print the log to both the screen and file, so I simulatered a
> 'tee'
>
> class Tee(file):
>
>     def __init__(self, name, mode):
>         file.__init__(self, name, mode)
>         self.stdout = sys.stdout
>         sys.stdout = self
>
>     def __del__(self):
>         sys.stdout = self.stdout
>         self.close()
>
>     def write(self, data):
>         file.write(self, data)
>         self.stdout.write(data)
>
> Tee('logfile', 'w')
> print >>sys.stdout, 'abcdefg'
>
> I found that it only output to the file, nothing to screen. Why?
> It seems the 'write' function was not called when I *print* something.

You create a Tee instance and it is immediately garbage collected. I'd
restore sys.stdout on Tee.close, not __del__ (you forgot to call the
inherited __del__ method, btw).
Mmm, doesn't work. I think there is an optimization somewhere: if it looks
like a real file object, it uses the original file write method, not yours.
The trick would be to use an object that does NOT inherit from file:

import sys
class TeeNoFile(object):
     def __init__(self, name, mode):
         self.file = open(name, mode)
         self.stdout = sys.stdout
         sys.stdout = self
     def close(self):
         if self.stdout is not None:
             sys.stdout = self.stdout
             self.stdout = None
         if self.file is not None:
             self.file.close()
             self.file = None
     def write(self, data):
         self.file.write(data)
         self.stdout.write(data)
     def flush(self):
         self.file.flush()
         self.stdout.flush()
     def __del__(self):
         self.close()

tee=TeeNoFile('logfile', 'w')
print 'abcdefg'
print 'another line'
tee.close()
print 'screen only'
del tee # should do nothing

--
Gabriel Genellina

1

cmd-line 스크립트를 실행하는 스크립트를 작성 중입니다. (경우에 따라 rsync의 경우와 같이 Linux 명령을 대신 할 수있는 대안이 없기 때문입니다.)

내가 정말로 원했던 것은 가능한 모든 경우에 기본 파이썬 로깅 메커니즘을 사용하는 것이었지만 예기치 않은 무언가 잘못되었을 때 여전히 오류를 포착하는 것이 었습니다.

이 코드는 트릭을 수행하는 것 같습니다. 특히 우아하거나 효율적이지 않을 수도 있습니다 (string + = string을 사용하지는 않지만 적어도 특정 병목 현상이 발생하지는 않습니다). 다른 사람에게 유용한 아이디어를 제공 할 수 있도록 게시하고 있습니다.

import logging
import os, sys
import datetime

# Get name of module, use as application name
try:
  ME=os.path.split(__file__)[-1].split('.')[0]
except:
  ME='pyExec_'

LOG_IDENTIFIER="uuu___( o O )___uuu "
LOG_IDR_LENGTH=len(LOG_IDENTIFIER)

class PyExec(object):

  # Use this to capture all possible error / output to log
  class SuperTee(object):
      # Original reference: http://mail.python.org/pipermail/python-list/2007-May/442737.html
      def __init__(self, name, mode):
          self.fl = open(name, mode)
          self.fl.write('\n')
          self.stdout = sys.stdout
          self.stdout.write('\n')
          self.stderr = sys.stderr

          sys.stdout = self
          sys.stderr = self

      def __del__(self):
          self.fl.write('\n')
          self.fl.flush()
          sys.stderr = self.stderr
          sys.stdout = self.stdout
          self.fl.close()

      def write(self, data):
          # If the data to write includes the log identifier prefix, then it is already formatted
          if data[0:LOG_IDR_LENGTH]==LOG_IDENTIFIER:
            self.fl.write("%s\n" % data[LOG_IDR_LENGTH:])
            self.stdout.write(data[LOG_IDR_LENGTH:])

          # Otherwise, we can give it a timestamp
          else:

            timestamp=str(datetime.datetime.now())
            if 'Traceback' == data[0:9]:
              data='%s: %s' % (timestamp, data)
              self.fl.write(data)
            else:
              self.fl.write(data)

            self.stdout.write(data)


  def __init__(self, aName, aCmd, logFileName='', outFileName=''):

    # Using name for 'logger' (context?), which is separate from the module or the function
    baseFormatter=logging.Formatter("%(asctime)s \t %(levelname)s \t %(name)s:%(module)s:%(lineno)d \t %(message)s")
    errorFormatter=logging.Formatter(LOG_IDENTIFIER + "%(asctime)s \t %(levelname)s \t %(name)s:%(module)s:%(lineno)d \t %(message)s")

    if logFileName:
      # open passed filename as append
      fl=logging.FileHandler("%s.log" % aName)
    else:
      # otherwise, use log filename as a one-time use file
      fl=logging.FileHandler("%s.log" % aName, 'w')

    fl.setLevel(logging.DEBUG)
    fl.setFormatter(baseFormatter)

    # This will capture stdout and CRITICAL and beyond errors

    if outFileName:
      teeFile=PyExec.SuperTee("%s_out.log" % aName)
    else:
      teeFile=PyExec.SuperTee("%s_out.log" % aName, 'w')

    fl_out=logging.StreamHandler( teeFile )
    fl_out.setLevel(logging.CRITICAL)
    fl_out.setFormatter(errorFormatter)

    # Set up logging
    self.log=logging.getLogger('pyExec_main')
    log=self.log

    log.addHandler(fl)
    log.addHandler(fl_out)

    print "Test print statement."

    log.setLevel(logging.DEBUG)

    log.info("Starting %s", ME)
    log.critical("Critical.")

    # Caught exception
    try:
      raise Exception('Exception test.')
    except Exception,e:
      log.exception(str(e))

    # Uncaught exception
    a=2/0


PyExec('test_pyExec',None)

분명히, 내가 당신처럼 기발하지 않을 경우, LOG_IDENTIFIER를 다른 사람이 로그에 쓰는 것을보고 싶지 않은 다른 문자열로 바꾸십시오.


0

모든 출력을 파일에 기록하고 텍스트 파일로 출력하려면 다음을 수행하십시오. 약간 해 키지 만 작동합니다.

import logging
debug = input("Debug or not")
if debug == "1":
    logging.basicConfig(level=logging.DEBUG, filename='./OUT.txt')
    old_print = print
    def print(string):
        old_print(string)
        logging.info(string)
print("OMG it works!")

편집 : sys.stderr을 sys.stdout으로 리디렉션하지 않으면 오류가 기록되지 않습니다.

EDIT2 : 두 번째 문제는 내장 함수와 달리 1 인수를 전달해야한다는 것입니다.

EDIT3 : 콘솔과 파일에 stdin과 stdout을 작성하기 전에 코드를 참조하십시오.

import logging, sys
debug = input("Debug or not")
if debug == "1":
    old_input = input
    sys.stderr.write = logging.info
    def input(string=""):
        string_in = old_input(string)
        logging.info("STRING IN " + string_in)
        return string_in
    logging.basicConfig(level=logging.DEBUG, filename='./OUT.txt')
    old_print = print
    def print(string="", string2=""):
        old_print(string, string2)
        logging.info(string)
        logging.info(string2)
print("OMG")
b = input()
print(a) ## Deliberate error for testing

-1

나는 대체 할 수 있도록 sys.stderr코드 를 완전히 교체 하고 이름 stderr을 바꿨다 .stdoutsys.stdout

이렇게하려면 내가 현재와 같은 객체 유형을 작성 stderr하고 stdout, 원래 시스템에 모든 방법을 전달 stderrstdout:

import os
import sys
import logging

class StdErrReplament(object):
    """
        How to redirect stdout and stderr to logger in Python
        /programming/19425736/how-to-redirect-stdout-and-stderr-to-logger-in-python

        Set a Read-Only Attribute in Python?
        /programming/24497316/set-a-read-only-attribute-in-python
    """
    is_active = False

    @classmethod
    def lock(cls, logger):
        """
            Attach this singleton logger to the `sys.stderr` permanently.
        """
        global _stderr_singleton
        global _stderr_default
        global _stderr_default_class_type

        # On Sublime Text, `sys.__stderr__` is set to None, because they already replaced `sys.stderr`
        # by some `_LogWriter()` class, then just save the current one over there.
        if not sys.__stderr__:
            sys.__stderr__ = sys.stderr

        try:
            _stderr_default
            _stderr_default_class_type

        except NameError:
            _stderr_default = sys.stderr
            _stderr_default_class_type = type( _stderr_default )

        # Recreate the sys.stderr logger when it was reset by `unlock()`
        if not cls.is_active:
            cls.is_active = True
            _stderr_write = _stderr_default.write

            logger_call = logger.debug
            clean_formatter = logger.clean_formatter

            global _sys_stderr_write
            global _sys_stderr_write_hidden

            if sys.version_info <= (3,2):
                logger.file_handler.terminator = '\n'

            # Always recreate/override the internal write function used by `_sys_stderr_write`
            def _sys_stderr_write_hidden(*args, **kwargs):
                """
                    Suppress newline in Python logging module
                    /programming/7168790/suppress-newline-in-python-logging-module
                """

                try:
                    _stderr_write( *args, **kwargs )
                    file_handler = logger.file_handler

                    formatter = file_handler.formatter
                    terminator = file_handler.terminator

                    file_handler.formatter = clean_formatter
                    file_handler.terminator = ""

                    kwargs['extra'] = {'_duplicated_from_file': True}
                    logger_call( *args, **kwargs )

                    file_handler.formatter = formatter
                    file_handler.terminator = terminator

                except Exception:
                    logger.exception( "Could not write to the file_handler: %s(%s)", file_handler, logger )
                    cls.unlock()

            # Only create one `_sys_stderr_write` function pointer ever
            try:
                _sys_stderr_write

            except NameError:

                def _sys_stderr_write(*args, **kwargs):
                    """
                        Hides the actual function pointer. This allow the external function pointer to
                        be cached while the internal written can be exchanged between the standard
                        `sys.stderr.write` and our custom wrapper around it.
                    """
                    _sys_stderr_write_hidden( *args, **kwargs )

        try:
            # Only create one singleton instance ever
            _stderr_singleton

        except NameError:

            class StdErrReplamentHidden(_stderr_default_class_type):
                """
                    Which special methods bypasses __getattribute__ in Python?
                    /programming/12872695/which-special-methods-bypasses-getattribute-in-python
                """

                if hasattr( _stderr_default, "__abstractmethods__" ):
                    __abstractmethods__ = _stderr_default.__abstractmethods__

                if hasattr( _stderr_default, "__base__" ):
                    __base__ = _stderr_default.__base__

                if hasattr( _stderr_default, "__bases__" ):
                    __bases__ = _stderr_default.__bases__

                if hasattr( _stderr_default, "__basicsize__" ):
                    __basicsize__ = _stderr_default.__basicsize__

                if hasattr( _stderr_default, "__call__" ):
                    __call__ = _stderr_default.__call__

                if hasattr( _stderr_default, "__class__" ):
                    __class__ = _stderr_default.__class__

                if hasattr( _stderr_default, "__delattr__" ):
                    __delattr__ = _stderr_default.__delattr__

                if hasattr( _stderr_default, "__dict__" ):
                    __dict__ = _stderr_default.__dict__

                if hasattr( _stderr_default, "__dictoffset__" ):
                    __dictoffset__ = _stderr_default.__dictoffset__

                if hasattr( _stderr_default, "__dir__" ):
                    __dir__ = _stderr_default.__dir__

                if hasattr( _stderr_default, "__doc__" ):
                    __doc__ = _stderr_default.__doc__

                if hasattr( _stderr_default, "__eq__" ):
                    __eq__ = _stderr_default.__eq__

                if hasattr( _stderr_default, "__flags__" ):
                    __flags__ = _stderr_default.__flags__

                if hasattr( _stderr_default, "__format__" ):
                    __format__ = _stderr_default.__format__

                if hasattr( _stderr_default, "__ge__" ):
                    __ge__ = _stderr_default.__ge__

                if hasattr( _stderr_default, "__getattribute__" ):
                    __getattribute__ = _stderr_default.__getattribute__

                if hasattr( _stderr_default, "__gt__" ):
                    __gt__ = _stderr_default.__gt__

                if hasattr( _stderr_default, "__hash__" ):
                    __hash__ = _stderr_default.__hash__

                if hasattr( _stderr_default, "__init__" ):
                    __init__ = _stderr_default.__init__

                if hasattr( _stderr_default, "__init_subclass__" ):
                    __init_subclass__ = _stderr_default.__init_subclass__

                if hasattr( _stderr_default, "__instancecheck__" ):
                    __instancecheck__ = _stderr_default.__instancecheck__

                if hasattr( _stderr_default, "__itemsize__" ):
                    __itemsize__ = _stderr_default.__itemsize__

                if hasattr( _stderr_default, "__le__" ):
                    __le__ = _stderr_default.__le__

                if hasattr( _stderr_default, "__lt__" ):
                    __lt__ = _stderr_default.__lt__

                if hasattr( _stderr_default, "__module__" ):
                    __module__ = _stderr_default.__module__

                if hasattr( _stderr_default, "__mro__" ):
                    __mro__ = _stderr_default.__mro__

                if hasattr( _stderr_default, "__name__" ):
                    __name__ = _stderr_default.__name__

                if hasattr( _stderr_default, "__ne__" ):
                    __ne__ = _stderr_default.__ne__

                if hasattr( _stderr_default, "__new__" ):
                    __new__ = _stderr_default.__new__

                if hasattr( _stderr_default, "__prepare__" ):
                    __prepare__ = _stderr_default.__prepare__

                if hasattr( _stderr_default, "__qualname__" ):
                    __qualname__ = _stderr_default.__qualname__

                if hasattr( _stderr_default, "__reduce__" ):
                    __reduce__ = _stderr_default.__reduce__

                if hasattr( _stderr_default, "__reduce_ex__" ):
                    __reduce_ex__ = _stderr_default.__reduce_ex__

                if hasattr( _stderr_default, "__repr__" ):
                    __repr__ = _stderr_default.__repr__

                if hasattr( _stderr_default, "__setattr__" ):
                    __setattr__ = _stderr_default.__setattr__

                if hasattr( _stderr_default, "__sizeof__" ):
                    __sizeof__ = _stderr_default.__sizeof__

                if hasattr( _stderr_default, "__str__" ):
                    __str__ = _stderr_default.__str__

                if hasattr( _stderr_default, "__subclasscheck__" ):
                    __subclasscheck__ = _stderr_default.__subclasscheck__

                if hasattr( _stderr_default, "__subclasses__" ):
                    __subclasses__ = _stderr_default.__subclasses__

                if hasattr( _stderr_default, "__subclasshook__" ):
                    __subclasshook__ = _stderr_default.__subclasshook__

                if hasattr( _stderr_default, "__text_signature__" ):
                    __text_signature__ = _stderr_default.__text_signature__

                if hasattr( _stderr_default, "__weakrefoffset__" ):
                    __weakrefoffset__ = _stderr_default.__weakrefoffset__

                if hasattr( _stderr_default, "mro" ):
                    mro = _stderr_default.mro

                def __init__(self):
                    """
                        Override any super class `type( _stderr_default )` constructor, so we can 
                        instantiate any kind of `sys.stderr` replacement object, in case it was already 
                        replaced by something else like on Sublime Text with `_LogWriter()`.

                        Assures all attributes were statically replaced just above. This should happen in case
                        some new attribute is added to the python language.

                        This also ignores the only two methods which are not equal, `__init__()` and `__getattribute__()`.
                    """
                    different_methods = ("__init__", "__getattribute__")
                    attributes_to_check = set( dir( object ) + dir( type ) )

                    for attribute in attributes_to_check:

                        if attribute not in different_methods \
                                and hasattr( _stderr_default, attribute ):

                            base_class_attribute = super( _stderr_default_class_type, self ).__getattribute__( attribute )
                            target_class_attribute = _stderr_default.__getattribute__( attribute )

                            if base_class_attribute != target_class_attribute:
                                sys.stderr.write( "    The base class attribute `%s` is different from the target class:\n%s\n%s\n\n" % (
                                        attribute, base_class_attribute, target_class_attribute ) )

                def __getattribute__(self, item):

                    if item == 'write':
                        return _sys_stderr_write

                    try:
                        return _stderr_default.__getattribute__( item )

                    except AttributeError:
                        return super( _stderr_default_class_type, _stderr_default ).__getattribute__( item )

            _stderr_singleton = StdErrReplamentHidden()
            sys.stderr = _stderr_singleton

        return cls

    @classmethod
    def unlock(cls):
        """
            Detach this `stderr` writer from `sys.stderr` and allow the next call to `lock()` create
            a new writer for the stderr.
        """

        if cls.is_active:
            global _sys_stderr_write_hidden

            cls.is_active = False
            _sys_stderr_write_hidden = _stderr_default.write



class StdOutReplament(object):
    """
        How to redirect stdout and stderr to logger in Python
        /programming/19425736/how-to-redirect-stdout-and-stderr-to-logger-in-python

        Set a Read-Only Attribute in Python?
        /programming/24497316/set-a-read-only-attribute-in-python
    """
    is_active = False

    @classmethod
    def lock(cls, logger):
        """
            Attach this singleton logger to the `sys.stdout` permanently.
        """
        global _stdout_singleton
        global _stdout_default
        global _stdout_default_class_type

        # On Sublime Text, `sys.__stdout__` is set to None, because they already replaced `sys.stdout`
        # by some `_LogWriter()` class, then just save the current one over there.
        if not sys.__stdout__:
            sys.__stdout__ = sys.stdout

        try:
            _stdout_default
            _stdout_default_class_type

        except NameError:
            _stdout_default = sys.stdout
            _stdout_default_class_type = type( _stdout_default )

        # Recreate the sys.stdout logger when it was reset by `unlock()`
        if not cls.is_active:
            cls.is_active = True
            _stdout_write = _stdout_default.write

            logger_call = logger.debug
            clean_formatter = logger.clean_formatter

            global _sys_stdout_write
            global _sys_stdout_write_hidden

            if sys.version_info <= (3,2):
                logger.file_handler.terminator = '\n'

            # Always recreate/override the internal write function used by `_sys_stdout_write`
            def _sys_stdout_write_hidden(*args, **kwargs):
                """
                    Suppress newline in Python logging module
                    /programming/7168790/suppress-newline-in-python-logging-module
                """

                try:
                    _stdout_write( *args, **kwargs )
                    file_handler = logger.file_handler

                    formatter = file_handler.formatter
                    terminator = file_handler.terminator

                    file_handler.formatter = clean_formatter
                    file_handler.terminator = ""

                    kwargs['extra'] = {'_duplicated_from_file': True}
                    logger_call( *args, **kwargs )

                    file_handler.formatter = formatter
                    file_handler.terminator = terminator

                except Exception:
                    logger.exception( "Could not write to the file_handler: %s(%s)", file_handler, logger )
                    cls.unlock()

            # Only create one `_sys_stdout_write` function pointer ever
            try:
                _sys_stdout_write

            except NameError:

                def _sys_stdout_write(*args, **kwargs):
                    """
                        Hides the actual function pointer. This allow the external function pointer to
                        be cached while the internal written can be exchanged between the standard
                        `sys.stdout.write` and our custom wrapper around it.
                    """
                    _sys_stdout_write_hidden( *args, **kwargs )

        try:
            # Only create one singleton instance ever
            _stdout_singleton

        except NameError:

            class StdOutReplamentHidden(_stdout_default_class_type):
                """
                    Which special methods bypasses __getattribute__ in Python?
                    /programming/12872695/which-special-methods-bypasses-getattribute-in-python
                """

                if hasattr( _stdout_default, "__abstractmethods__" ):
                    __abstractmethods__ = _stdout_default.__abstractmethods__

                if hasattr( _stdout_default, "__base__" ):
                    __base__ = _stdout_default.__base__

                if hasattr( _stdout_default, "__bases__" ):
                    __bases__ = _stdout_default.__bases__

                if hasattr( _stdout_default, "__basicsize__" ):
                    __basicsize__ = _stdout_default.__basicsize__

                if hasattr( _stdout_default, "__call__" ):
                    __call__ = _stdout_default.__call__

                if hasattr( _stdout_default, "__class__" ):
                    __class__ = _stdout_default.__class__

                if hasattr( _stdout_default, "__delattr__" ):
                    __delattr__ = _stdout_default.__delattr__

                if hasattr( _stdout_default, "__dict__" ):
                    __dict__ = _stdout_default.__dict__

                if hasattr( _stdout_default, "__dictoffset__" ):
                    __dictoffset__ = _stdout_default.__dictoffset__

                if hasattr( _stdout_default, "__dir__" ):
                    __dir__ = _stdout_default.__dir__

                if hasattr( _stdout_default, "__doc__" ):
                    __doc__ = _stdout_default.__doc__

                if hasattr( _stdout_default, "__eq__" ):
                    __eq__ = _stdout_default.__eq__

                if hasattr( _stdout_default, "__flags__" ):
                    __flags__ = _stdout_default.__flags__

                if hasattr( _stdout_default, "__format__" ):
                    __format__ = _stdout_default.__format__

                if hasattr( _stdout_default, "__ge__" ):
                    __ge__ = _stdout_default.__ge__

                if hasattr( _stdout_default, "__getattribute__" ):
                    __getattribute__ = _stdout_default.__getattribute__

                if hasattr( _stdout_default, "__gt__" ):
                    __gt__ = _stdout_default.__gt__

                if hasattr( _stdout_default, "__hash__" ):
                    __hash__ = _stdout_default.__hash__

                if hasattr( _stdout_default, "__init__" ):
                    __init__ = _stdout_default.__init__

                if hasattr( _stdout_default, "__init_subclass__" ):
                    __init_subclass__ = _stdout_default.__init_subclass__

                if hasattr( _stdout_default, "__instancecheck__" ):
                    __instancecheck__ = _stdout_default.__instancecheck__

                if hasattr( _stdout_default, "__itemsize__" ):
                    __itemsize__ = _stdout_default.__itemsize__

                if hasattr( _stdout_default, "__le__" ):
                    __le__ = _stdout_default.__le__

                if hasattr( _stdout_default, "__lt__" ):
                    __lt__ = _stdout_default.__lt__

                if hasattr( _stdout_default, "__module__" ):
                    __module__ = _stdout_default.__module__

                if hasattr( _stdout_default, "__mro__" ):
                    __mro__ = _stdout_default.__mro__

                if hasattr( _stdout_default, "__name__" ):
                    __name__ = _stdout_default.__name__

                if hasattr( _stdout_default, "__ne__" ):
                    __ne__ = _stdout_default.__ne__

                if hasattr( _stdout_default, "__new__" ):
                    __new__ = _stdout_default.__new__

                if hasattr( _stdout_default, "__prepare__" ):
                    __prepare__ = _stdout_default.__prepare__

                if hasattr( _stdout_default, "__qualname__" ):
                    __qualname__ = _stdout_default.__qualname__

                if hasattr( _stdout_default, "__reduce__" ):
                    __reduce__ = _stdout_default.__reduce__

                if hasattr( _stdout_default, "__reduce_ex__" ):
                    __reduce_ex__ = _stdout_default.__reduce_ex__

                if hasattr( _stdout_default, "__repr__" ):
                    __repr__ = _stdout_default.__repr__

                if hasattr( _stdout_default, "__setattr__" ):
                    __setattr__ = _stdout_default.__setattr__

                if hasattr( _stdout_default, "__sizeof__" ):
                    __sizeof__ = _stdout_default.__sizeof__

                if hasattr( _stdout_default, "__str__" ):
                    __str__ = _stdout_default.__str__

                if hasattr( _stdout_default, "__subclasscheck__" ):
                    __subclasscheck__ = _stdout_default.__subclasscheck__

                if hasattr( _stdout_default, "__subclasses__" ):
                    __subclasses__ = _stdout_default.__subclasses__

                if hasattr( _stdout_default, "__subclasshook__" ):
                    __subclasshook__ = _stdout_default.__subclasshook__

                if hasattr( _stdout_default, "__text_signature__" ):
                    __text_signature__ = _stdout_default.__text_signature__

                if hasattr( _stdout_default, "__weakrefoffset__" ):
                    __weakrefoffset__ = _stdout_default.__weakrefoffset__

                if hasattr( _stdout_default, "mro" ):
                    mro = _stdout_default.mro

                def __init__(self):
                    """
                        Override any super class `type( _stdout_default )` constructor, so we can 
                        instantiate any kind of `sys.stdout` replacement object, in case it was already 
                        replaced by something else like on Sublime Text with `_LogWriter()`.

                        Assures all attributes were statically replaced just above. This should happen in case
                        some new attribute is added to the python language.

                        This also ignores the only two methods which are not equal, `__init__()` and `__getattribute__()`.
                    """
                    different_methods = ("__init__", "__getattribute__")
                    attributes_to_check = set( dir( object ) + dir( type ) )

                    for attribute in attributes_to_check:

                        if attribute not in different_methods \
                                and hasattr( _stdout_default, attribute ):

                            base_class_attribute = super( _stdout_default_class_type, self ).__getattribute__( attribute )
                            target_class_attribute = _stdout_default.__getattribute__( attribute )

                            if base_class_attribute != target_class_attribute:
                                sys.stdout.write( "    The base class attribute `%s` is different from the target class:\n%s\n%s\n\n" % (
                                        attribute, base_class_attribute, target_class_attribute ) )

                def __getattribute__(self, item):

                    if item == 'write':
                        return _sys_stdout_write

                    try:
                        return _stdout_default.__getattribute__( item )

                    except AttributeError:
                        return super( _stdout_default_class_type, _stdout_default ).__getattribute__( item )

            _stdout_singleton = StdOutReplamentHidden()
            sys.stdout = _stdout_singleton

        return cls

    @classmethod
    def unlock(cls):
        """
            Detach this `stdout` writer from `sys.stdout` and allow the next call to `lock()` create
            a new writer for the stdout.
        """

        if cls.is_active:
            global _sys_stdout_write_hidden

            cls.is_active = False
            _sys_stdout_write_hidden = _stdout_default.write

이를 사용하려면 출력 텍스트를 보내는 데 사용하려는 로거를 호출 StdErrReplament::lock(logger)하고 StdOutReplament::lock(logger)전달하면됩니다. 예를 들면 다음과 같습니다.

import os
import sys
import logging

current_folder = os.path.dirname( os.path.realpath( __file__ ) )
log_file_path = os.path.join( current_folder, "my_log_file.txt" )

file_handler = logging.FileHandler( log_file_path, 'a' )
file_handler.formatter = logging.Formatter( "%(asctime)s %(name)s %(levelname)s - %(message)s", "%Y-%m-%d %H:%M:%S" )

log = logging.getLogger( __name__ )
log.setLevel( "DEBUG" )
log.addHandler( file_handler )

log.file_handler = file_handler
log.clean_formatter = logging.Formatter( "", "" )

StdOutReplament.lock( log )
StdErrReplament.lock( log )

log.debug( "I am doing usual logging debug..." )
sys.stderr.write( "Tests 1...\n" )
sys.stdout.write( "Tests 2...\n" )

이 코드를 실행하면 화면에 다음과 같이 표시됩니다.

여기에 이미지 설명을 입력하십시오

그리고 파일 내용에 :

여기에 이미지 설명을 입력하십시오

log.debug화면 에서 통화 내용도 보려면 로거에 스트림 핸들러를 추가해야합니다. 이 경우 다음과 같습니다.

import os
import sys
import logging

class ContextFilter(logging.Filter):
    """ This filter avoids duplicated information to be displayed to the StreamHandler log. """
    def filter(self, record):
        return not "_duplicated_from_file" in record.__dict__

current_folder = os.path.dirname( os.path.realpath( __file__ ) )
log_file_path = os.path.join( current_folder, "my_log_file.txt" )

stream_handler = logging.StreamHandler()
file_handler = logging.FileHandler( log_file_path, 'a' )

formatter = logging.Formatter( "%(asctime)s %(name)s %(levelname)s - %(message)s", "%Y-%m-%d %H:%M:%S" )
file_handler.formatter = formatter
stream_handler.formatter = formatter
stream_handler.addFilter( ContextFilter() )

log = logging.getLogger( __name__ )
log.setLevel( "DEBUG" )
log.addHandler( file_handler )
log.addHandler( stream_handler )

log.file_handler = file_handler
log.stream_handler = stream_handler
log.clean_formatter = logging.Formatter( "", "" )

StdOutReplament.lock( log )
StdErrReplament.lock( log )

log.debug( "I am doing usual logging debug..." )
sys.stderr.write( "Tests 1...\n" )
sys.stdout.write( "Tests 2...\n" )

실행할 때 다음과 같이 출력됩니다.

여기에 이미지 설명을 입력하십시오

여전히 파일에 저장하는 동안 my_log_file.txt:

여기에 이미지 설명을 입력하십시오

이 기능을 사용 중지하면 다른 사람이 이전 버전에 대한 참조를 가질 수 있기 때문에 연결된 로거를 분리 할 수 ​​없으므로 스트림 StdErrReplament:unlock()의 표준 동작 만 복원합니다 stderr. 이것이 결코 죽을 수없는 세계적인 싱글 톤 인 이유입니다. 따라서이 모듈을 imp다른 것으로 재로드하는 경우, 이미 모듈에 sys.stderr주입 된 전류 를 다시 캡처하여 내부에 저장하지 않습니다.


5
스트림 복제를위한 놀라운 수준의 우연한 복잡성.
Attila Lendvai
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.