TATECHATLAS
◎ Русский
Программирование

Логирование исключений в Python: сохраняйте traceback и достаточно контекста запроса

Используйте именованный регистратор, настройте приложение один раз и добавляйте безопасный контекст, не дублируя каждую ошибку.

В этом материале

Внутри обработчика исключения logger.exception записывает событие уровня ERROR с текущей информацией об исключении. Настройте обработчики в точке входа приложения и используйте getLogger(__name__) в отдельных модулях. Добавьте безопасный идентификатор операции или запроса, чтобы по traceback можно было связать сбой с конкретным действием. Логирование фиксирует неудачу; программа должна отдельно решить, восстановиться ли ей, вернуть ошибку или повторно вызвать исключение.

Прочитайте полный небольшой пример

Этот созданный автономный скрипт намеренно передаёт нечисловую строку в int. Ожидаемый лог содержит сообщение ERROR с request_id=demo1 и информацией об исключении, заканчивающейся ValueError. Точные пути traceback и номера строк зависят от того, где сохранён скрипт, поэтому полный фиксированный traceback не гарантируется. Пример демонстрирует вызов логирования; перехваченное исключение не распространяется автоматически к вызывающей стороне.

Выберите событие, которое нужно записать

Объект исключения и операционное событие отвечают на разные вопросы. Исключение описывает, что не удалось; событие может идентифицировать, какое действие было в процессе. Перед добавлением логирования решите, кто будет читать запись и что ему потребуется для расследования. Короткое сообщение с именем операции и несекретным идентификатором корреляции обычно полезнее, чем повторение всего тела запроса или вывод всех локальных переменных.

Настройте логирование на границе приложения

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

Используйте информацию об исключении внутри обработчика

logger.exception предназначен для обработчика исключения, где доступна текущая информация об исключении. Вызов logger.error только с текстом исключения пропускает traceback, если информация об исключении не запрошена явно. Напротив, запись traceback не требует считать каждое восстанавливаемое условие фатальной ошибкой приложения. Выбирайте уровень события и политику восстановления согласно операции, а не только согласно имени класса исключения.

import logging

logging.basicConfig(level=logging.INFO, format="%(levelname)s %(name)s %(message)s")
logger = logging.getLogger(__name__)
request_id = "demo1"
try:
    int("bad")
except ValueError:
    logger.exception("Could not parse quantity; request_id=%s", request_id)

Решите, кто владеет итоговой записью об ошибке

Если нижний слой записывает исключение и повторно вызывает его, а верхний слой тоже его записывает, один сбой может появиться дважды. Решите, какой слой имеет достаточно контекста для создания операционной записи. Нижний слой может добавлять информацию, вызывая подходящее исключение, а граница записывает итоговый сбой. Это выбор проектирования, а не требование, что каждое исключение должно быть записано ровно один раз везде.

Диагностируйте повторяющийся вывод через обработчики

Записи, похожие на дубликаты, могут появиться и при прикреплении обработчика к дочернему регистратору при разрешении распространения к предку, у которого есть другой обработчик. Исследуйте иерархию регистраторов и конфигурацию обработчиков, прежде чем удалять события приложения. Уровни регистратора и обработчика могут влиять на появление вывода. Подавление распространения без понимания назначений может скрыть записи из центрального приёмника так же, как удалить дублирующую консольную строку.

Исключите чувствительный контекст из записи

Используйте непрозрачный идентификатор запроса или тщательно выбранное имя операции. Не включайте пароли, заголовки авторизации, ключи API или всё нефильтрованное полезное нагрузку запроса. Сами сообщения исключений могут содержать пользовательский ввод или детали подключения, поэтому безопасный вызов логирования не гарантирует, что весь результирующий traceback безопасен для хранения. Применяйте политики доступа, хранения и сокрытия, подходящие реальному приложению и назначению логов.

Разделяйте диагностику и поведение программы

Вызов логирования ни повторяет операцию, ни выбирает ответ, возвращаемый клиенту. Зафиксировав событие, намеренно решите, продолжить ли с допустимым резервным вариантом, вернуть документированную ошибку или повторно вызвать исключение. Зафиксируйте выбор рядом с границей ошибки. Если программа продолжается, не делайте последующий код зависимым от значения, которое не было успешно создано неудавшимся оператором.

Что проверить

  • Настройте обработчики в приложении, а не в каждом модуле.
  • Используйте информацию об исключении, пока обрабатываете исключение.
  • Добавьте безопасный идентификатор корреляции.
  • Проверяйте обработчики и распространение, если вывод повторяется.
  • Определите восстановление или распространение отдельно от логирования.

Скрипт иллюстрирует логирование из стандартной библиотеки и перехваченное ValueError. Это не полная производственная конфигурация логирования и не демонстрирует развёрнутый приёмник логов, автоматическое сокрытие данных, повторы или выполненное тестирование приложения.

Источники

  1. Python: logging reference ↗
  2. Python: logging how-to ↗
Наверх ↑