7

Как зарегистрировать ошибку в Python с отладочной информацией?

5

Я записываю сообщения об ошибках Python в лог-файл с помощью функции logging.error:

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

Можно ли вывести более подробную информацию об исключении и коде, который его вызвал, чем просто строку исключения? Такие данные, как номера строк или трассировки стека, были бы очень полезны.

5 ответ(ов)

2

Использование параметра exc_info может быть более предпочтительным, так как он позволяет выбрать уровень ошибки (если вы используете exception, он всегда будет на уровне error):

try:
    # выполнить какое-то действие здесь
except Exception as e:
    logging.critical(e, exc_info=True)  # логируем информацию об исключении на уровне CRITICAL
0

Да, вы можете записать стек вызовов без исключения, используя параметр stack_info в методах журналирования. По умолчанию этот параметр установлен в False, но если установить его в True, информация о стеке добавляется к сообщению журнала.

Важно отметить, что эта информация о стеке отличается от той, которую вы получаете при использовании параметра exc_info: первая показывает фреймы стека от самого низкого уровня до вызова журналирования в текущем потоке, тогда как вторая содержит информацию о фреймах стека, которые были развёрнуты в результате исключения, когда вы ищете обработчики исключений.

Вот пример использования:

import logging

logging.basicConfig(level=logging.DEBUG)
logging.getLogger().info('Это выводит стек', stack_info=True)

Если вы выполните этот код, вы увидите сообщение журнала вместе со стеком вызовов, что поможет вам понять, откуда именно был выполнен вызов журналирования.

0

С начала Python 3.5 появилась возможность явно указывать исключение в функции логирования:

try:
    1 / 0
except Exception as ex:
    logging.error("Произошла ошибка", exc_info=ex)

Функция logging.exception по-прежнему работает, но её необходимо вызывать только в блоке except, и она не позволяет задавать уровень логирования.

0

Этот ответ основывается на вышеуказанных отличных ответах.

В большинстве приложений вы не будете напрямую вызывать 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.

0

Вы можете воспользоваться декоратором 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, или сделать его универсальным, добавив параметр, который задает это значение.

Используйте этот подход, чтобы обеспечить более надежную обработку ошибок в своем коде!

Чтобы ответить на вопрос, пожалуйста, войдите или зарегистрируйтесь