programing

sys.stdout을 로그 파일로 복제하는 방법은 무엇입니까?

muds 2023. 6. 30. 22:39
반응형

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"는 화면에 표시된 것과 같아야 합니다.

이전에도 이와 같은 문제가 있었는데 이 스니펫이 매우 유용하다는 것을 알게 되었습니다.

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()에 할당하는 개체의 입니다.

나는 동시에 두 곳에 편지를 쓰기 위해 작은 수업을 할 것입니다.

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)

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

  • 로그 파일 이름을 매개 변수로 지정해야 합니다.
  • sys.을 sys.stdout으로 할 입니다.<stdout>프로그램이 진행되는 동안 로그를 기록하지 않을 경우.
  • 한 번에 여러 로그 파일에 쓰거나 다른 로그 수준을 처리하는 등의 기능을 원할 수 있습니다.

이 모든 것들은 독자들을 위한 연습으로 남겨두는 것이 편할 정도로 간단합니다.여기서 핵심 통찰력은print " 객체"파일 같은 객체"라고 sys.stdout.

코에서외프를쉽스생수있로으므성할게부드세로,▁use▁since,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)

은 당이정원는것은입니다.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), 구성원/속성/설정 특성 등을 생략합니다.하지만, 그것은 아마도 현재 상태로는 대부분의 사람들에게 충분히 좋을 것입니다.

일반성 외에 제가 좋아하는 것은 직접 전화를 걸지 않는다는 점에서 깨끗하다는 것입니다.write,flush,os.dup2 타기.

저는 이 질문이 반복적으로 답변되었다는 것을 알고 있지만, 이를 위해 저는 John T의 답변에서 주요 답변을 가져와서 제안된 플러시를 포함하도록 수정하고 링크된 수정 버전을 따릅니다.cladmi의 답변에서 언급한 대로 진입과 퇴장을 위드문과 함께 사용하기 위해 추가하였습니다.또한 설명서에는 다음을 사용하여 파일을 플러시하는 방법이 나와 있습니다.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()

다른 곳에서 설명한 것처럼 로깅 모듈을 직접 사용하는 것이 가장 좋은 해결책일 수 있습니다.

import logging

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

그러나 실제로 stdout을 리디렉션하려는 경우는 드문 경우도 있습니다.print를 사용하는 django의 runserver 명령을 확장할 때 이런 상황이 발생했습니다.나는 장고 소스를 해킹하고 싶지 않았지만 파일로 가기 위해 인쇄문이 필요했습니다.

다음은 로깅 모듈을 사용하여 셸에서 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'

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

John T의 답변 작성하기: https://stackoverflow.com/a/616686/395687

추가했습니다.__enter__그리고.__exit__컨텍스트 관리자로 사용하는 방법with에 " 이코를제는키워드하공드,키드▁this워이▁which▁keyword▁code"를 부여하는

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')

나는 썼습니다.tee()대부분의 경우에 사용할 수 있는 Python에서의 구현 및 Windows에서도 사용할 수 있습니다.

https://github.com/pycontribs/tendo

또한, 당신은 그것과 함께 사용할 수 있습니다.logging파이썬의 모듈입니다.

(아, 질문을 다시 읽고 해당되지 않는지 확인하십시오.)

여기 파이썬 로깅 모듈을 사용하는 샘플 프로그램이 있습니다.이 로깅 모듈은 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())

Jacob Gabrielson이 승인한 솔루션을 약 1년 동안 사용했지만, 이제 피할 수 없는 일이 발생했고 사용자 중 한 명이 Windows에서 이 솔루션을 원합니다.다른 제안된 답변을 보면, 이러한 답변의 대부분은 생성된 프로세스의 출력을 캡처하는 데 실패한다고 생각합니다(원본 포스터에서 굵은 글씨로 표시됨).제 생각에 이것을 하는 유일한 방법은os.dup2()Unix 전용 도구를 사용하지 않고 원본 포스터의 정확한 질문에 답변하는 방법을 알게 된 것 같습니다.tee이제 생성된 셸 명령을 포함하여 Python 프로그램의 모든 출력을 캡처할 수 있습니다.이것은 Windows, Mac 및 Linux에서 작동합니다.코드는 다음과 같습니다.

import os, sys, threading, platform

class StreamCapture:
    def __init__(self,stream,writer,echo=True,monkeypatch=None):
        self.active = True
        self.writer = writer
        self.stream = stream
        self.fd = stream.fileno()
        self.echo = echo
        (r,w) = os.pipe()
        self.pipe_read_fd = r
        self.pipe_write_fd = w
        self.dup_fd = os.dup(self.fd)
        os.dup2(w,self.fd)
        self.monkeypatch = monkeypatch if monkeypatch is not None else platform.system()=='Windows'
        if self.monkeypatch:
            self.oldwrite = stream.write
            stream.write = lambda z: os.write(self.fd,z.encode() if type(z)==str else z)
        t = threading.Thread(target=self.printer)
        self.thread = t
        t.start()
    def printer(self):
        while True:
            data = os.read(self.pipe_read_fd,100000)
            if(len(data)==0):
                self.writer.close()
                os.close(self.dup_fd)
                os.close(self.pipe_read_fd)
                return
            self.writer.write(data)
            if self.echo:
                os.write(self.dup_fd,data)
    def close(self):
        if not self.active:
            return
        self.active = False
        self.stream.flush()
        if self.monkeypatch:
            self.stream.write = self.oldwrite
        os.dup2(self.dup_fd,self.fd)
        os.close(self.pipe_write_fd)
    def __enter__(self):
        return self
    def __exit__(self,a,b,c):
        self.close()

이를 다음과 같이 사용합니다(Jacob Gabrielson의 솔루션을 제외한 다른 솔루션이 캡처하지 못하는 어려운 경우).

print("This does not get saved to the log file")
with StreamCapture(sys.stdout,open('logfile.txt','wb')):
        os.write(sys.stdout.fileno(),b"Hello, captured world!\n")
        os.system('echo Hello from the shell')     # Hard case
        print("More capturing")
print("This also does not get saved to the log file")

이것은 간단명료한 대답은 아니지만, 저는 그것을 간결하게 하려고 노력했고, 그것은 제가 할 수 있는 한 간단합니다.다음과 같은 이유로 복잡합니다.

  1. ▁를 사용할 수 없기 때문에.tee든 나는어그수합니다야해행일을든떻의 을 해야 .tee내 파이썬 프로세스 내에서.휴대할 수 있는 방법이 있는지 확실하지 않았습니다.fork() 및 신통및신통os.pipe()(이것은 Windows에서 분기된 프로세스와 파일 설명자를 공유하기 어렵다는 것을 나타냅니다) 그래서 저는 사용하기로 결정했습니다.threading.

  2. Windows 경sys.stdout그리고.sys.stderr그들의 근본이 언제인지 정말 이해하지 못합니다.fileno()를 경로를 합니다.os.pipe()경유로os.dup2() Python ▁after다▁immedi▁python▁the▁interpre▁crashes니 직후 Python 인터프리터가 충돌합니다.print(...)지휘권

  3. 충돌을 I Windows 서에인충해결기하를사해용다합니위만터돌터을리프p▁on▁i에▁monkey다▁windowsp,ter▁the▁crashesatch,사▁windows▁solve용▁interpre▁only니▁to.sys.stdout.write = ...단순히 호출하는 새로운 기능으로 설정함으로써.os.write(...)기본적으로 Windows(윈도우)가 검색된 경우에만 이 작업을 수행합니다.를 하기 에, 저는 이 "monkeypatch", "monkeypatch"에 대한 모든 sys.stdout저는 새로운 스트림을 할당하는 대신 이 원숭이 패치 방식을 선택했습니다.sys.stdout=...오래된 것들의 복제품들이 걱정되었기 때문입니다.sys.stdout통역사의 다양한 부분에 캐시되겠지만, 저는 추측했습니다.sys.stdout.write직접 캐시되었을 가능성이 낮았습니다.

  4. 파이프의 출력을 처리하는 스레드를 데몬화하면 주 스레드가 완료되는 즉시 해당 스레드가 삭제되지만 모든 출력이 로그 파일에 기록된 것은 아닙니다.실제로 이러한 도우미 스레드를 데몬화하지 않고 파이프가 닫힐 때 정상적으로 종료되도록 해야 합니다.

저는 사실 모든 코너 케이스를 제대로 이해했는지 확신할 수 없습니다. 섬세한 OS 기능과 상호 작용하는 스레드 코드는 쓰기가 무섭습니다.그럼에도 불구하고 지금까지 제 시험을 통과했습니다.털이 많아서 PyPI 패키지를 만들었습니다.

pip install streamcapture

깃허브가 왔습니다.

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

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'

위의 답변 중 어떤 것도 제기된 문제에 실제로 답하지 않는 것 같습니다.이것이 오래된 스레드라는 것은 알지만, 이 문제는 모든 사람들이 만드는 것보다 훨씬 더 간단하다고 생각합니다.

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.

@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

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를 누군가가 로그에 쓰는 것을 보고 싶지 않은 다른 문자열로 바꾸세요.

추가할 수도 있습니다.stderr또한, 위의 shx2의 답변에 기초하여 다음을 사용합니다.class multifile:

class Log(object):

    def __init__(self, path_log, mode="w", encoding="utf-8"):
        h = open(path_log, mode, encoding=encoding)
        sys.stdout = multifile([ sys.stdout, h ])
        sys.stderr = multifile([ sys.stderr, h ])

    def __enter__(self):
        """ Necessary if called by with (or with... as) """
        return self     # only necessary if "as"

    def __exit__(self, type, value, tb):
        """ Necessary if call by with """
        pass

    def __del__(self):
        if sys is not None:
            # restoring
            sys.stdout = sys.__stdout__
            sys.stderr = sys.__stderr__

log = Log("test.txt")
print("line 1")
print("line 2", file=sys.stderr)
del log
print("line 3 only on screen")

여기 stdout을 일시적으로 파일에 복제하는 컨텍스트 관리자가 있습니다.재설정하기 때문에 제 관점에서 개선되었습니다.sys.stdout예외가 발생하더라도 파일을 닫으며 구문은 백그라운드에서 보이지 않는 변경을 나타냅니다.John T의 솔루션을 확장했습니다.

class DuplicateStdout:
    def __init__(self, path):
        self.stdout = sys.stdout
        self.path = path
        self.f = None
    
    def write(self, s):
        self.stdout.write(s)
        self.f.write(s)

    def __enter__(self):
        self.f = open(self.path, "w")
        sys.stdout = self
    
    def __exit__(self, *args):
        sys.stdout = self.stdout
        self.f.close()

사용 예:

with DuplicateStdout("foo.log"):
    print("Hey") # also in foo.log

print("There") # not in foo.log

나는 다음을 위해 완전한 대체물을 썼습니다.sys.stderr코드 이름 변경을 복제했습니다.stderrstdout▁를 대체할 수 하기 위해.sys.stdout.

를 위해 의 이를위현동객일체만유와 동일한 .stderr그리고.stdout 시스템으로 전달합니다.stderr그리고.stdout:

import os
import sys
import logging

class StdErrReplament(object):
    """
        How to redirect stdout and stderr to logger in Python
        https://stackoverflow.com/questions/19425736/how-to-redirect-stdout-and-stderr-to-logger-in-python

        Set a Read-Only Attribute in Python?
        https://stackoverflow.com/questions/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
                    https://stackoverflow.com/questions/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?
                    https://stackoverflow.com/questions/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
        https://stackoverflow.com/questions/19425736/how-to-redirect-stdout-and-stderr-to-logger-in-python

        Set a Read-Only Attribute in Python?
        https://stackoverflow.com/questions/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
                    https://stackoverflow.com/questions/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?
                    https://stackoverflow.com/questions/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" )

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

enter image description here

파일 내용:

enter image description here

당신도 그 내용을 보고 싶다면,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" )

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

enter image description here

할 수 .my_log_file.txt:

enter image description here

이 기능을 사용하지 않도록 설정할 때StdErrReplament:unlock() 단지표행복뿐입다니원할의 표준 동작만 합니다.stderr스트림. 다른 사용자가 이전 버전에 대한 참조를 가질 수 있기 때문에 연결된 로거를 분리할 수 없습니다.이것이 그것이 결코 죽을 수 없는 세계적인 싱글톤인 이유입니다.본 을 "" "" "로 다시 로드하는 imp아니면 다른 것, 그것은 결코 물살을 되찾지 못할 것입니다.sys.stderr이미 주입되어 내부적으로 저장되어 있기 때문입니다.

언급URL : https://stackoverflow.com/questions/616645/how-to-duplicate-sys-stdout-to-a-log-file

반응형