역 추적 로그 예외


답변:


203

처리기 / 블록 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

1
장고 코드를 살펴본 결과 대답이 '아니요'라고 가정하고 있지만 특정 문자 또는 깊이의 양으로 역 추적을 제한하는 방법이 있습니까? 문제는 큰 역 추적의 경우 꽤 오래 걸린다는 것입니다.
에두아르 루카

10
로거를 정의하면 이 작업을 수행하기 logger = logging.getLogger('yourlogger')위해 작성 logger.exception('...')해야합니다.
576i

메시지가 로그 레벨 INFO로 인쇄되도록이를 수정할 수 있습니까?
NM

Azure 통찰력과 같은 특정 외부 앱의 경우 트랙백이 로그에 저장되지 않습니다. 그런 다음 아래 표시된대로 명시 적으로 메시지 문자열에 전달해야합니다.
Edgar H

139

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

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

나는 exc_info=kwarg가 무엇인지 기억할 수 없다 . 감사!
berto

4
유형이 중복으로 두 번 기록된다는 점을 제외하면 logging.exception과 동일합니다. 오류 이외의 수준을 원하지 않는 한 logging.exception을 사용하십시오.
Wyrmwood

@Wyrmwood 님에게 메시지를 보내야하는 것과 동일하지 않습니다logging.exception
Peter Wood

57

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


2
꽤 흥미로운 접근법. 한 가지 질문- add_custom_print_exception링크 한 사이트에없는 것 같습니다. 대신 다른 최종 코드가 있습니다. 어느 쪽이 더 좋고 더 최종적이며 왜 그럴까요? 감사!
fantabolous

고마워, 좋은 대답!
101

잘라서 붙여 넣기 오타가 있습니다. old_print_exception에 대한 위임 된 호출에서 limit 및 file은 None이 아니라 limit 및 file로 전달되어야합니다.-old_print_exception (etype, value, tb, limit, file)
Marvin

마지막 코드 블록의 경우 StringIO를 초기화하고 예외를 인쇄하는 대신 logger.error(traceback.format_tb())(또는 예외 정보를 원하면 format_exc ()를 호출 하면됩니다.
James

8

파이썬의 로깅 함수의 매개 변수를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


3

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

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

./test.py 2>> mylog.log

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

./test.py &> mylog.log


2

모든 레벨 (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)

1

sys.excepthook을 사용하는 버전은 다음과 같습니다.

import traceback
import sys

logger = logging.getLogger()

def handle_excepthook(type, message, stack):
     logger.error(f'An unhandled exception occured: {message}. Traceback: {traceback.format_tb(stack)}')

sys.excepthook = handle_excepthook

{traceback.format_exc()}대신 에 사용 하는 것은 {traceback.format_tb(stack)}어떻습니까?
변수

0

아마도 세련되지는 않지만 더 쉬울 수도 있습니다.

#!/bin/bash
log="/var/log/yourlog"
/path/to/your/script.py 2>&1 | (while read; do echo "$REPLY" >> $log; done)

-1

다음은 Python 2.6 설명서 에서 가져온 간단한 예입니다 .

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

4
문제는 역 추적 로그인하는 방법이었다
콘스탄틴 슈베르트
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.