디버그 정보로 Python 오류를 어떻게 기록합니까?


468

다음을 사용하여 Python 예외 메시지를 로그 파일에 인쇄하고 있습니다 logging.error.

import logging
try:
    1/0
except ZeroDivisionError as e:
    logging.error(e)  # ERROR:root:division by zero

예외 문자열보다 예외 및 예외를 생성 한 코드에 대한 자세한 정보를 인쇄 할 수 있습니까? 줄 번호 또는 스택 추적과 같은 것이 좋습니다.

답변:


733

logger.exception 오류 메시지와 함께 스택 추적을 출력합니다.

예를 들면 다음과 같습니다.

import logging
try:
    1/0
except ZeroDivisionError as e:
    logging.exception("message")

산출:

ERROR:root:message
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
ZeroDivisionError: integer division or modulo by zero

@Paulo Check 메모, "Python 3 logging.exception에서는 except부분 내부 에서 메소드 를 호출해야합니다 . 임의의 위치에서이 메소드를 호출하면 기괴한 예외가 발생할 수 있습니다. 문서는 이에 대해 경고합니다."


131
exception메소드는 단순히를 호출합니다 error(message, exc_info=1). exc_info예외 컨텍스트에서 로깅 방법 중 하나에 전달하자마자 역 추적을받습니다.
Helmut Grohne

16
try / except에서 모든 코드를 래핑하지 않도록 설정 sys.excepthook( 여기 참조 ) 할 수도 있습니다 .
jul

23
당신은 어떤 식 으로든 except Exception:사용하지 않기 때문에 글을 쓸 수 있습니다 e;)
Marco Ferrari

21
e대화식으로 코드를 디버깅하려고 할 때 검사하는 것이 좋습니다. :) 이것이 항상 포함시키는 이유입니다.
Vicki Laidler

4
내가 틀렸다면 정정하십시오.이 경우 예외를 실제로 처리하지 않으므로 범위 raise의 끝에 추가 하는 것이 except좋습니다. 그렇지 않으면 모든 것이 정상인 것처럼 계속 실행됩니다.
Dror

184

에 대한 하나의 좋은 일이 logging.exception있음을 SiggyF의 대답은 표시되지 않습니다 당신이 임의의 메시지를 전달할 수 있으며, 기록은 여전히 예외 세부 사항 전체 역 추적을 보여줄 것입니다 :

import logging
try:
    1/0
except ZeroDivisionError:
    logging.exception("Deliberate divide by zero traceback")

에 오류를 인쇄하는 기본 (최근 버전) 로깅 동작을 사용 sys.stderr하면 다음과 같습니다.

>>> import logging
>>> try:
...     1/0
... except ZeroDivisionError:
...     logging.exception("Deliberate divide by zero traceback")
... 
ERROR:root:Deliberate divide by zero traceback
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
ZeroDivisionError: integer division or modulo by zero

메시지를 제공하지 않고 예외를 기록 할 수 있습니까?
Stevoisiak

@StevenVascellaro- ''메시지를 정말로 입력하고 싶지 않으면 전달할 것을 제안합니다 ...하지만 적어도 하나의 인수없이 함수를 호출 할 수 없으므로 무언가를 제공해야합니다.
ArtOfWarfare 2018 년

147

exc_info오류 수준을 선택할 수 있도록 옵션을 사용하는 것이 좋습니다 (를 사용 exception하는 경우 항상 error수준에 있음).

try:
    # do something here
except Exception as e:
    logging.critical(e, exc_info=True)  # log exception info at CRITICAL log level

@CivFan : 다른 편집본이나 게시물 소개를 실제로 보지 않았습니다. 이 소개는 타사 편집자에 의해 추가되었습니다. 삭제 된 댓글에서 의도했던 내용을 볼 수 없지만 편집을 취소하고 댓글을 제거 할 수 있습니다. 여기에서 투표 한 내용이 편집 된 버전 이외의 다른 항목에 비해 너무 오래되었습니다. .
Martijn Pieters

logging.fatal로깅 라이브러리의 방법은? 나만 볼 수 critical있습니다.
Ian

1
@Ian이 별칭이다 critical처럼 warn이다 warning.
0xc0de

35

인용

logging모듈을 사용하지 않고 애플리케이션이 다른 방식으로 로깅하는 경우 어떻게 합니까?

이제 traceback여기서 사용할 수 있습니다.

import traceback

def log_traceback(ex, ex_traceback=None):
    if ex_traceback is None:
        ex_traceback = ex.__traceback__
    tb_lines = [ line.rstrip('\n') for line in
                 traceback.format_exception(ex.__class__, ex, ex_traceback)]
    exception_logger.log(tb_lines)
  • 파이썬 2 에서 사용하십시오 :

    try:
        # your function call is here
    except Exception as ex:
        _, _, ex_traceback = sys.exc_info()
        log_traceback(ex, ex_traceback)
  • 파이썬 3 에서 사용하십시오 :

    try:
        x = get_number()
    except Exception as ex:
        log_traceback(ex)

왜 "_, _, ex_traceback = sys.exc_info ()"를 log_traceback 함수 외부에 놓고 인수로 전달 했습니까? 함수 내에서 직접 사용하지 않습니까?
바질 무사

@BasilMusa는이 때문에, 파이썬 3 호환에, 짧은에, 귀하의 질문에 대답 ex_traceback에서입니다 ex.__traceback__파이썬 3에서, 그러나 ex_traceback출신 sys.exc_info()파이썬 2. 아래
zangw

12

일반 로그를 사용하는 경우 모든 로그 레코드는이 규칙과 일치해야합니다 one record = one line. 이 규칙에 따라 grep및 기타 도구를 사용 하여 로그 파일을 처리 할 수 ​​있습니다.

그러나 역 추적 정보는 여러 줄입니다. 그래서 내 대답은 이 스레드에서 위의 zangw가 제안한 확장 버전의 솔루션 입니다. 문제는 역 추적 라인이 \n내부에 있을 수 있으므로이 라인 엔딩을 제거하기 위해 추가 작업을 수행해야한다는 것입니다.

import logging


logger = logging.getLogger('your_logger_here')

def log_app_error(e: BaseException, level=logging.ERROR) -> None:
    e_traceback = traceback.format_exception(e.__class__, e, e.__traceback__)
    traceback_lines = []
    for line in [line.rstrip('\n') for line in e_traceback]:
        traceback_lines.extend(line.splitlines())
    logger.log(level, traceback_lines.__str__())

그 후 (로그를 분석 할 때) 로그 파일에서 필요한 역 추적 라인을 복사하여 붙여 넣을 수 있습니다.

ex_traceback = ['line 1', 'line 2', ...]
for line in ex_traceback:
    print(line)

이익!


9

이 답변은 위의 우수한 답변에서 작성됩니다.

대부분의 응용 프로그램에서는 logging.exception (e)을 직접 호출하지 않습니다. 다음과 같이 애플리케이션 또는 모듈에 특정한 사용자 정의 로거를 정의했을 것입니다.

# Set the name of the app or module
my_logger = logging.getLogger('NEM Sequencer')
# Set the log level
my_logger.setLevel(logging.INFO)

# Let's say we want to be fancy and log to a graylog2 log server
graylog_handler = graypy.GELFHandler('some_server_ip', 12201)
graylog_handler.setLevel(logging.INFO)
my_logger.addHandler(graylog_handler)

이 경우 로거를 사용하여 다음과 같이 예외 (e)를 호출하십시오.

try:
    1/0
except ZeroDivisionError, e:
    my_logger.exception(e)

이것은 예외를 위해 전용 로거를 원한다면 실제로 유용한 마무리입니다.
logicOnAbstractions

7

예외없이 스택 추적을 기록 할 수 있습니다.

https://docs.python.org/3/library/logging.html#logging.Logger.debug

두 번째 선택적 키워드 인수는 stack_info이며 기본값은 False입니다. true 인 경우 실제 로깅 호출을 포함하여 스택 정보가 로깅 메시지에 추가됩니다. 이것은 exc_info 지정을 통해 표시되는 것과 동일한 스택 정보는 아닙니다. 예외 처리기를 검색하는 동안 예외를 따릅니다.

예:

>>> import logging
>>> logging.basicConfig(level=logging.DEBUG)
>>> logging.getLogger().info('This prints the stack', stack_info=True)
INFO:root:This prints the stack
Stack (most recent call last):
  File "<stdin>", line 1, in <module>
>>>

5

약간의 데코레이터 처리 (아마도 모나드와 리프팅에서 느슨하게 영감을 얻음). Python 3.6 유형 주석을 안전하게 제거하고 이전 메시지 형식 스타일을 사용할 수 있습니다.

fallible.py

from functools import wraps
from typing import Callable, TypeVar, Optional
import logging


A = TypeVar('A')


def fallible(*exceptions, logger=None) \
        -> Callable[[Callable[..., A]], Callable[..., Optional[A]]]:
    """
    :param exceptions: a list of exceptions to catch
    :param logger: pass a custom logger; None means the default logger, 
                   False disables logging altogether.
    """
    def fwrap(f: Callable[..., A]) -> Callable[..., Optional[A]]:

        @wraps(f)
        def wrapped(*args, **kwargs):
            try:
                return f(*args, **kwargs)
            except exceptions:
                message = f'called {f} with *args={args} and **kwargs={kwargs}'
                if logger:
                    logger.exception(message)
                if logger is None:
                    logging.exception(message)
                return None

        return wrapped

    return fwrap

데모:

In [1] from fallible import fallible

In [2]: @fallible(ArithmeticError)
    ...: def div(a, b):
    ...:     return a / b
    ...: 
    ...: 

In [3]: div(1, 2)
Out[3]: 0.5

In [4]: res = div(1, 0)
ERROR:root:called <function div at 0x10d3c6ae8> with *args=(1, 0) and **kwargs={}
Traceback (most recent call last):
  File "/Users/user/fallible.py", line 17, in wrapped
    return f(*args, **kwargs)
  File "<ipython-input-17-e056bd886b5c>", line 3, in div
    return a / b

In [5]: repr(res)
'None'

이 솔루션을 수정 None하여 except부품 보다 조금 더 의미있는 것을 반환 할 수도 있습니다 (또는 fallible인수 에이 반환 값을 지정하여 솔루션을 일반으로 만들 수도 있습니다 ).


0

로깅 모듈 (사용자 정의 모듈 인 경우)에서 stack_info를 활성화하십시오.

api_logger.exceptionLog("*Input your Custom error message*",stack_info=True)

-1

추가 종속성에 대처할 수있는 경우 twisted.log를 사용하면 오류를 명시 적으로 기록 할 필요가 없으며 파일이나 스트림에 전체 추적 및 시간을 반환합니다.


8
아마도 twisted좋은 추천이지만이 답변은 실제로 많은 기여를하지는 않습니다. 사용 방법 twisted.log이나 logging표준 라이브러리 의 모듈에 비해 어떤 이점이 있는지 , "명시 적으로 오류를 기록 할 필요가 없음" 의 의미를 설명 하지는 않습니다 .
Mark Amery

-8

그것을하는 확실한 방법 format_exc()은 출력을 사용 하고 파싱하여 관련 부분을 얻는 것입니다.

from traceback import format_exc

try:
    1/0
except Exception:
    print 'the relevant part is: '+format_exc().split('\n')[-2]

문안 인사


4
응? 왜 "관련 부분" 입니까? (가) 모든 .split('\n')[-2]수행은 멀리 던져 의 결과에서 행 번호와 추적을 format_exc()당신이 일반적으로 원하는 유용한 정보 -! 무엇보다, 심지어의 좋은 일하지 않는 것을 ; 예외 메시지에 줄 바꿈이 포함 된 경우이 방법은 예외 메시지의 마지막 줄만 인쇄합니다. 즉, 예외 클래스와 대부분의 예외 메시지를 잃어 버리면 트레이스 백이 손실됩니다. -1.
Mark Amery
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.