Как зарегистрировать ошибку в Python с отладочной информацией?
Я записываю сообщения об ошибках Python в лог-файл с помощью функции logging.error
:
import logging
try:
1/0
except ZeroDivisionError as e:
logging.error(e) # ERROR:root:division by zero
Можно ли вывести более подробную информацию об исключении и коде, который его вызвал, чем просто строку исключения? Такие данные, как номера строк или трассировки стека, были бы очень полезны.
5 ответ(ов)
Использование параметра exc_info
может быть более предпочтительным, так как он позволяет выбрать уровень ошибки (если вы используете exception
, он всегда будет на уровне error
):
try:
# выполнить какое-то действие здесь
except Exception as e:
logging.critical(e, exc_info=True) # логируем информацию об исключении на уровне CRITICAL
Да, вы можете записать стек вызовов без исключения, используя параметр stack_info
в методах журналирования. По умолчанию этот параметр установлен в False
, но если установить его в True
, информация о стеке добавляется к сообщению журнала.
Важно отметить, что эта информация о стеке отличается от той, которую вы получаете при использовании параметра exc_info
: первая показывает фреймы стека от самого низкого уровня до вызова журналирования в текущем потоке, тогда как вторая содержит информацию о фреймах стека, которые были развёрнуты в результате исключения, когда вы ищете обработчики исключений.
Вот пример использования:
import logging
logging.basicConfig(level=logging.DEBUG)
logging.getLogger().info('Это выводит стек', stack_info=True)
Если вы выполните этот код, вы увидите сообщение журнала вместе со стеком вызовов, что поможет вам понять, откуда именно был выполнен вызов журналирования.
С начала Python 3.5 появилась возможность явно указывать исключение в функции логирования:
try:
1 / 0
except Exception as ex:
logging.error("Произошла ошибка", exc_info=ex)
Функция logging.exception
по-прежнему работает, но её необходимо вызывать только в блоке except
, и она не позволяет задавать уровень логирования.
Этот ответ основывается на вышеуказанных отличных ответах.
В большинстве приложений вы не будете напрямую вызывать logging.exception(e)
. Скорее всего, вы определили свой собственный логгер, специфичный для вашего приложения или модуля, вот так:
# Задаем имя приложения или модуля
my_logger = logging.getLogger('NEM Sequencer')
# Устанавливаем уровень логирования
my_logger.setLevel(logging.INFO)
# Предположим, что мы хотим быть немного изящными и отправлять логи на сервер graylog2
graylog_handler = graypy.GELFHandler('some_server_ip', 12201)
graylog_handler.setLevel(logging.INFO)
my_logger.addHandler(graylog_handler)
В этом случае просто используйте логгер для вызова exception(e)
следующим образом:
try:
1/0
except ZeroDivisionError as e:
my_logger.exception(e)
Обратите внимание на использование as
для захвата исключения. Это более современный и предпочтительный стиль, начиная с Python 3.
Вы можете воспользоваться декоратором fallible
для обработки исключений в вашем коде, что позволит вам безопасно обрабатывать ошибки и делать это с помощью логирования. Этот декоратор позволяет указать список исключений, которые он будет перехватывать, и опционально использовать пользовательский логгер.
Вот как это работает:
fallible.py
from functools import wraps
import logging
def fallible(*exceptions, logger=None):
"""
:param exceptions: список исключений для перехвата
:param logger: передайте пользовательский логгер; None означает использование стандартного логгера,
False отключает логгирование вовсе.
"""
def fwrap(f):
@wraps(f)
def wrapped(*args, **kwargs):
try:
return f(*args, **kwargs)
except exceptions:
message = 'called {} with *args={} and **kwargs={}'.format(f, args, kwargs)
if logger:
logger.exception(message)
if logger is None:
logging.exception(message)
return None
return wrapped
return fwrap
Пример использования:
from fallible import fallible
@fallible(ArithmeticError)
def div(a, b):
return a / b
print(div(1, 2)) # Output: 0.5
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
print(repr(res)) # Output: 'None'
В результате, если функция вызовет исключение, вы получите сообщение об ошибке в журнале, а возвращаемое значение будет None
. Вы также можете настроить декоратор, чтобы он возвращал более значимое значение вместо None
в блоке except
, или сделать его универсальным, добавив параметр, который задает это значение.
Используйте этот подход, чтобы обеспечить более надежную обработку ошибок в своем коде!
Сохранение сообщений об исключениях в Python
Конфигурация логгера для записи в файл и вывода на stdout
Вручную вызов (бросание) исключения в Python
Как напечатать исключение в Python?
Как проверить, существует ли переменная?