[python] 역 추적 로그 예외

파이썬 오류를 어떻게 기록합니까?

try:
    do_something()
except:
    # How can I log my exception here, complete with its traceback?



답변

처리기 / 블록 logging.exception내에서 사용 except:하여 메시지 앞에 추가 된 추적 정보와 함께 현재 예외를 기록합니다.

import logging
LOG_FILENAME = '/tmp/logging_example.out'
logging.basicConfig(filename=LOG_FILENAME, level=logging.DEBUG)

logging.debug('This message should go to the log file')

try:
    run_my_stuff()
except:
    logging.exception('Got exception on main handler')
    raise

이제 로그 파일을 살펴보십시오 /tmp/logging_example.out.

DEBUG:root:This message should go to the log file
ERROR:root:Got exception on main handler
Traceback (most recent call last):
  File "/tmp/teste.py", line 9, in <module>
    run_my_stuff()
NameError: name 'run_my_stuff' is not defined


답변

사용 exc_info옵션이 더 좋을 수도 있고 경고 또는 오류 제목으로 남아 있습니다.

try:
    # coode in here
except Exception as e:
    logging.error(e, exc_info=True)


답변

내 직업은 최근 응용 프로그램에서 모든 역 추적 / 예외를 기록하는 작업을 맡았습니다. 위와 같은 다른 사람들이 온라인에 게시 한 수많은 기술을 시도했지만 다른 방법으로 해결했습니다. 재정의 traceback.print_exception.

나는 http://www.bbarrows.com/에 글을 썼다. 훨씬 더 읽기 쉽지만 여기에 붙여 넣을 것이다.

우리 소프트웨어가 야생에서 발생할 수있는 모든 예외를 기록하는 작업을 할 때 파이썬 예외 추적을 기록하기 위해 여러 가지 다른 기술을 시도했습니다. 처음에는 파이썬 시스템 예외 후크, sys.excepthook이 로깅 코드를 삽입하기에 완벽한 장소라고 생각했습니다. 나는 비슷한 것을 시도하고 있었다 :

import traceback
import StringIO
import logging
import os, sys

def my_excepthook(excType, excValue, traceback, logger=logger):
    logger.error("Logging an uncaught exception",
                 exc_info=(excType, excValue, traceback))

sys.excepthook = my_excepthook  

이것은 메인 스레드에서 작동했지만 곧 내 sys.excepthook이 프로세스가 시작한 새 스레드에 존재하지 않는다는 것을 알았습니다. 대부분의 모든 것이이 프로젝트의 스레드에서 발생하기 때문에 이것은 큰 문제입니다.

인터넷 검색 및 많은 문서를 읽은 후 가장 유용한 정보는 Python Issue tracker에서 얻은 것입니다.

스레드의 첫 번째 게시물은 스레드 sys.excepthook간에 NOT 지속 의 실제 예를 보여줍니다 (아래 참조). 분명히 이것은 예상 된 동작입니다.

import sys, threading

def log_exception(*args):
    print 'got exception %s' % (args,)
sys.excepthook = log_exception

def foo():
    a = 1 / 0

threading.Thread(target=foo).start()

이 Python Issue 스레드의 메시지는 실제로 2 개의 제안 된 핵을 생성합니다. Thread예외를 잡아서 기록하기 위해 서브 클래스 와 run 메소드를 자체 try except 블록으로 랩핑하거나 예외를 차단하고 예외를 로그하기 위해 원숭이 패치 threading.Thread.run를 시도하십시오.

서브 클래 싱의 첫 번째 방법은 Thread사용자 정의 Thread클래스 를 가져오고 사용해야 할 때마다 로깅 스레드를 원했기 때문에 코드에서 우아하지 않은 것 같습니다 . 전체 코드베이스를 검색하고 모든 노멀 Threads을이 커스텀으로 바꿔야했기 때문에 이것은 번거 로움이되었습니다 Thread. 그러나 이것이 무엇 Thread을하고 있는지는 분명 했으며 사용자 지정 로깅 코드에 문제가있는 경우 누군가 진단하고 디버그하기가 더 쉬울 것입니다. 사용자 정의 로깅 스레드는 다음과 같습니다.

class TracebackLoggingThread(threading.Thread):
    def run(self):
        try:
            super(TracebackLoggingThread, self).run()
        except (KeyboardInterrupt, SystemExit):
            raise
        except Exception, e:
            logger = logging.getLogger('')
            logger.exception("Logging an uncaught exception")

원숭이 패치의 두 번째 방법은 threading.Thread.run바로 한 번만 실행하고 __main__모든 예외에서 로깅 코드를 계측 할 수 있기 때문에 좋습니다 . Monkey patching은 무언가의 예상되는 기능을 변경함에 따라 디버깅하기가 성 가실 수 있습니다. 파이썬 이슈 트래커에서 제안 된 패치는 다음과 같습니다.

def installThreadExcepthook():
    """
    Workaround for sys.excepthook thread bug
    From
http://spyced.blogspot.com/2007/06/workaround-for-sysexcepthook-bug.html

(https://sourceforge.net/tracker/?func=detail&atid=105470&aid=1230540&group_id=5470).
    Call once from __main__ before creating any threads.
    If using psyco, call psyco.cannotcompile(threading.Thread.run)
    since this replaces a new-style class method.
    """
    init_old = threading.Thread.__init__
    def init(self, *args, **kwargs):
        init_old(self, *args, **kwargs)
        run_old = self.run
        def run_with_except_hook(*args, **kw):
            try:
                run_old(*args, **kw)
            except (KeyboardInterrupt, SystemExit):
                raise
            except:
                sys.excepthook(*sys.exc_info())
        self.run = run_with_except_hook
    threading.Thread.__init__ = init

예외 로깅 테스트를 시작하기 전까지는 모든 것이 잘못되었다는 것을 깨달았습니다.

테스트하기 위해

raise Exception("Test")

내 코드 어딘가에. 그러나이 메소드를 호출 한 메소드는 랩백을 인쇄하고 예외를 삼킨 블록을 제외하고 시도했습니다. 역 추적 가져 오기가 STDOUT에 인쇄되었지만 기록되지 않았기 때문에 매우 실망 스럽습니다. 그런 다음 트레이스 백을 기록하는 훨씬 쉬운 방법은 모든 파이썬 코드가 트레이스 백 자체를 인쇄하는 데 사용하는 방법 인 traceback.print_exception을 원숭이 패치하는 것입니다. 나는 다음과 비슷한 결과를 얻었습니다.

def add_custom_print_exception():
    old_print_exception = traceback.print_exception
    def custom_print_exception(etype, value, tb, limit=None, file=None):
        tb_output = StringIO.StringIO()
        traceback.print_tb(tb, limit, tb_output)
        logger = logging.getLogger('customLogger')
        logger.error(tb_output.getvalue())
        tb_output.close()
        old_print_exception(etype, value, tb, limit=None, file=None)
    traceback.print_exception = custom_print_exception

이 코드는 트레이스 백을 문자열 버퍼에 기록하고 ERROR 로깅에 기록합니다. 사용자 지정 로깅 처리기가 오류 수준 로그를 가져 와서 분석을 위해 집으로 보내는 ‘customLogger’로거를 설정했습니다.


답변

파이썬의 로깅 함수의 매개 변수를sys.excepthook 사용하여 핸들러를 할당하여 메인 스레드에서 포착되지 않은 모든 예외를 기록 할 수 있습니다 .exc_info

import sys
import logging

logging.basicConfig(filename='/tmp/foobar.log')

def exception_hook(exc_type, exc_value, exc_traceback):
    logging.error(
        "Uncaught exception",
        exc_info=(exc_type, exc_value, exc_traceback)
    )

sys.excepthook = exception_hook

raise Exception('Boom')

그러나 프로그램이 스레드를 사용하는 경우 Python의 이슈 트래커의 이슈 1230540 에 언급 된대로 catch되지 않은 예외가 발생하면이를 사용하여 작성된 스레드 threading.Thread가 트리거 되지 않습니다 . 원어를 블록으로 감싸고 블록 내부에서 호출 하는 대체 방법 으로 덮어 쓰는 원숭이 패치와 같은 일부 제한 사항은이 제한을 해결하기 위해 제안되었습니다 . 또는, 당신은 수동으로 당신의 스레드 각각에 대한 진입 점 포장 수 / 자신을.sys.excepthookThread.__init__self.runruntrysys.excepthookexcepttryexcept


답변

잡히지 않은 예외 메시지는 STDERR로 전달되므로 Python 자체에서 로깅을 구현하는 대신 Python 스크립트를 실행하는 데 사용하는 쉘을 사용하여 STDERR을 파일로 보낼 수 있습니다. Bash 스크립트에서는 BASH 안내서에 설명 된대로 출력 리디렉션을 사용하여이 작업을 수행 할 수 있습니다 .

파일에 오류를 추가하고 터미널에 다른 출력을 추가하십시오.

./test.py 2>> mylog.log

인터리브 된 STDOUT 및 STDERR 출력으로 파일을 덮어 씁니다.

./test.py &> mylog.log


답변

내가 찾던 것 :

import sys
import traceback

exc_type, exc_value, exc_traceback = sys.exc_info()
traceback_in_var = traceback.format_tb(exc_traceback)

보다:


답변

모든 레벨 (DEBUG, INFO, …)에서 로거를 사용하여 역 추적을 얻을 수 있습니다. 를 사용 logging.exception하면 레벨이 ERROR입니다.

# test_app.py
import sys
import logging

logging.basicConfig(level="DEBUG")

def do_something():
    raise ValueError(":(")

try:
    do_something()
except Exception:
    logging.debug("Something went wrong", exc_info=sys.exc_info())
DEBUG:root:Something went wrong
Traceback (most recent call last):
  File "test_app.py", line 10, in <module>
    do_something()
  File "test_app.py", line 7, in do_something
    raise ValueError(":(")
ValueError: :(

편집하다:

이것도 작동합니다 (파이썬 3.6 사용)

logging.debug("Something went wrong", exc_info=True)