Как зарегистрировать ошибку Python с отладочной информацией?
я печатаю сообщения об исключениях Python в файл журнала с помощью logging.error:
import logging
try:
1/0
except ZeroDivisionError as e:
logging.error(e) # ERROR:root:division by zero
можно ли напечатать более подробную информацию об исключении и коде, который его сгенерировал, чем просто строку исключения? Такие вещи, как номера строк или трассировки стека, были бы великолепны.
9 ответов:
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 Cheque Примечания, " имейте в виду, что в Python 3 Вы должны вызвать
logging.exceptionметод только внутриexceptчасть. Если вы вызываете этот метод в произвольном месте, вы можете получить странное исключение. Врачи предупреждают об этом."
одна хорошая вещь о
logging.exceptionэто ответ Сиггифа не показывает, что вы можете передать произвольное сообщение, и ведение журнала все равно покажет полную обратную трассировку со всеми деталями исключения: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
использование параметров exc_info может быть лучше, чтобы вы могли выбрать уровень ошибки (если вы используете
exception, Он всегда будет показыватьerror):try: # do something here except Exception as e: logging.fatal(e, exc_info=True) # log exception info at FATAL log level
Что делать, если ваше приложение делает ведение журнала каким – то другим способом-не используя
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)
использовать его в Python 2:
try: # your function call is here except Exception as ex: _, _, ex_traceback = sys.exc_info() log_traceback(ex, ex_traceback)использовать его в Python 3:
try: x = get_number() except Exception as ex: log_traceback(ex)
если вы используете простые журналы - все ваши записи журнала должны соответствовать этому правилу:
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)профит!
этот ответ создает из вышесказанного безупречным.
в большинстве приложений вы не будете вызывать ведение журнала.исключение(Е) напрямую. Скорее всего, вы определили пользовательский регистратор, специфичный для вашего приложения или модуля следующим образом:
# 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)
немного обработки декоратора (очень свободно воодушевленный возможно монадой и подниматься). Вы можете безопасно удалить аннотации типа 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' s аргументы.)
Если вы можете справиться с дополнительной зависимостью, то используйте twisted.журнал, вам не нужно явно регистрировать ошибки, а также он возвращает всю обратную трассировку и время в файл или поток.
чистый способ сделать это с помощью
format_exc()а затем разобрать вывод, чтобы получить соответствующую часть:from traceback import format_exc try: 1/0 except Exception: print 'the relevant part is: '+format_exc().split('\n')[-2]в отношении
Comments