Как мне зарегистрировать ошибку Python с отладочной информацией?
Я печатаю сообщения об исключениях Python в файл журнала с logging.error
:
import logging
try:
1/0
except ZeroDivisionError as e:
logging.error(e) # ERROR:root:division by zero
Можно ли напечатать более подробную информацию об исключении и коде, который его сгенерировал, чем просто строку исключения? Такие вещи, как номера строк или трассировки стека, были бы великолепны.
Ответы
Ответ 1
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 Проверьте заметки, "быть в курсе, что в Python 3 вы должны вызвать logging.exception
метод только внутри, за except
части. Если вы вызываете этот метод в произвольном месте вы можете получить причудливое исключение. Документы оповещения об этом."
Ответ 2
Одна хорошая вещь в logging.exception
, которую не показывает ответ SiggyF, заключается в том, что вы можете передать произвольное сообщение, и при ведении журнала все равно будет отображаться полный возврат со всеми деталями исключения:
import logging
try:
1/0
except ZeroDivisionError:
logging.exception("Deliberate divide by zero traceback")
По умолчанию (в последних версиях) ведение журнала просто sys.stderr
ошибки в 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
Ответ 3
Использование параметров 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
Ответ 4
Цитата
Что делать, если ваше приложение регистрируется каким-либо другим способом - не используя модуль 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)
Ответ 5
Если вы используете простые журналы - все ваши записи журнала должны соответствовать этому правилу: 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)
Profit!
Ответ 6
Этот ответ строится из вышеупомянутых отличных.
В большинстве приложений вы не будете вызывать 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 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)
Ответ 7
Немного декораторской обработки (очень слабо вдохновлено монадой Maybe и лифтингом). Вы можете безопасно удалять аннотации типа 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
аргументах).
Ответ 8
Если вы можете справиться с дополнительной зависимостью, используйте twisted.log, вам не нужно явно регистрировать ошибки, а также возвращает всю трассировку и время на файл или поток.
Ответ 9
Чистый способ сделать это - использовать format_exc()
, а затем проанализировать вывод, чтобы получить соответствующую часть:
from traceback import format_exc
try:
1/0
except Exception:
print 'the relevant part is: '+format_exc().split('\n')[-2]
Привет