Рубрики
Без рубрики

Сообщение об исключениях в скриптах Python с помощью Sentry

Узнайте, как перехватить исключения в сценариях Python, а затем использовать Sentry для хранения и анализа ошибок.

Автор оригинала: Matt Makai.

Скрипты Python – это клей, который удерживает многие приложения и их инфраструктура работает, но когда один из ваших скриптов выдает исключение вы можете не узнать об этом сразу, если у вас нет централизованного места для агрегировать ошибки. Вот где добавление Sentry может решить эту проблему с распределенной регистрацией ошибок.

В этом руководстве мы увидим, как быстро добавить Sentry в новый или существующий Скрипт Python для централизованного сообщения об ошибках для дальнейшего отладка.

Настройка среды разработки

Убедитесь, что у вас установлен Python 3. На данный момент Python 3.8.3 – последняя версия версия Python.

В этом уроке мы также будем использовать:

Установите вышеуказанные библиотеки кода в новый Виртуальная среда Python используя следующие команды:

python -m venv sentryscript
source sentryscript/bin/activate

pip install sentry-sdk>=0.14.4

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

Обратите внимание, что весь код этого руководства можно найти в примеры-кода-блога Репозиторий Git на GitHub под python-script-sentry каталог.

Пример сценария для загрузки модулей Python

Мы начнем с написания небольшого, но полезного скрипта, который распечатывает имена всех модулей в пакете Python, затем добавьте к нему Sentry когда становится очевидным, что захват исключений будет полезное дополнение.

Создайте новый файл с именем module_loader.py и напишите следующие строки кода в нем, чтобы мы могли легко его выполнить в командной строке.

import argparse

def import_submodules(package):
    return {}


if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument("package")
    args = parser.parse_args()

    package_to_load = args.package
    results = import_submodules(package_to_load)
    for r in results:
        print(str(r))

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

Затем добавьте следующие выделенные строки кода, чтобы использовать importlib и pkgutil для рекурсивного импорта модулей из пакета, если он найдено, что соответствует имени, переданному в качестве аргумента package .

import argparse
import importlib
import pkgutil


def import_submodules(package):
    """Import all submodules of a module, recursively, including subpackages.

    :param package: package (name or actual module)
    :type package: str | module
    :rtype: dict[str, types.ModuleType]
    """
    if isinstance(package, str):
        package = importlib.import_module(package)
    results = {}
    for loader, name, is_pkg in pkgutil.walk_packages(package.__path__):
        full_name = package.__name__ + '.' + name
        try:
            results[full_name] = importlib.import_module(full_name)
            if is_pkg:
                results.update(import_submodules(full_name))
        except ModuleNotFoundError as mnfe:
            print("module not found: {}".format(full_name))
        except Exception as general_exception:
            print(general_exception)
    return results


if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument("package")
    args = parser.parse_args()

    package_to_load = args.package
    results = import_submodules(package_to_load)
    for r in results:
        print(str(r))

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

Протестируйте полный сценарий, чтобы увидеть, что он распечатывает с произвольным пакет в командной строке:

python module_loader.py importlib

В приведенном выше примере создается вывод:

importlib._bootstrap
importlib._bootstrap_external
importlib.abc
importlib.machinery
importlib.resources
importlib.util

Попытка проверить пакет, который не установлен, приведет к ошибке. Использовать сценарий с пакетом, который не установлен в вашей текущей среде.

python module_loader.py flask

Вышеупомянутая команда производит следующую трассировку из-за ожидаемого ModuleNotFoundError .

Traceback (most recent call last):
  File "module_loader.py", line 35, in 
    results = import_submodules(package_to_load)
  File "module_loader.py", line 14, in import_submodules
    package = importlib.import_module(package)
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/importlib/__init__.py", line 127, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
  File "", line 1006, in _gcd_import
  File "", line 983, in _find_and_load
  File "", line 965, in _find_and_load_unlocked
ModuleNotFoundError: No module named 'flask'

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

Наш пример скрипта можно использовать, но что, если мы запустим этот код или что-то подобное на одном или нескольких серверах, которые мы не так часто проверяем? Вот где это будет быть полезным, чтобы иметь способ объединить вывод исключения одного или нескольких сценариев в одном месте. Sentry может помочь нам в достижении этой цели.

Добавление отчетов об исключениях с помощью Sentry

Sentry может быть самостоятельно размещенным или используется в качестве облачной службы через Sentry.io . В этом мы будем использовать облачную версию, потому что она быстрее, чем настройка собственного сервера, а также бесплатно для небольших проектов.

Перейдите на главную страницу Sentry.io .

Домашняя страница Sentry.io, на которой вы можете создать бесплатную учетную запись.

Войдите в свою учетную запись или зарегистрируйте новую бесплатную учетную запись. Вы будете в главная панель управления учетной записью после входа в систему или завершения знака Sentry вверх процесс.

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

Пустая панель управления учетной записью Sentry.

Вы захотите создать новый проект Sentry Project только для этого приложения, чтобы нажмите «Проекты» на левой боковой панели, чтобы перейти на страницу «Проекты».

Кнопка для создания нового проекта Sentry.

На странице “Проекты” нажмите кнопку “Создать проект” в правом верхнем углу. угол страницы.

Создать новый проект Sentry.

Выберите Python, дайте вашему новому проекту имя и нажмите «Создать проект». кнопка. Наш новый проект готов к интеграции с нашим скриптом Python.

Нам нужен уникальный идентификатор для нашей учетной записи и проекта для авторизации нашего Код Python для отправки ошибок этому экземпляру Sentry. Самый простой способ получить нам нужно пойти в страница документации по началу работы с Python и прокрутите вниз до раздела «Настроить SDK».

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

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

export SENTRY_DSN='https://yourkeygoeshere.ingest.sentry.io/project-number'

Обязательно замените yourkeygoeshere своим уникальным идентификатором и “номер проекта” с идентификатором, который соответствует проекту, который вы только что создан.

Убедитесь, что SENTRY_DSN правильно установлен в вашей оболочке, используя echo команда:

echo $SENTRY_DSN

Измените приложение для отправки информации об исключении в Sentry прямо сейчас что у нас есть уникальный идентификатор. Снова откройте module_loader.py и обновите следующие выделенные строки кода.

import argparse
import importlib
import os
import pkgutil
import sentry_sdk
from sentry_sdk import capture_exception

# find on https://docs.sentry.io/error-reporting/quickstart/?platform=python
sentry_sdk.init(dsn=os.getenv('SENTRY_DSN'))


def import_submodules(package):
    """Import all submodules of a module, recursively, including subpackages.

    :param package: package (name or actual module)
    :type package: str | module
    :rtype: dict[str, types.ModuleType]
    """
    if isinstance(package, str):
        package = importlib.import_module(package)
    results = {}
    for loader, name, is_pkg in pkgutil.walk_packages(package.__path__):
        full_name = package.__name__ + '.' + name
        try:
            results[full_name] = importlib.import_module(full_name)
            if is_pkg:
                results.update(import_submodules(full_name))
        except ModuleNotFoundError as mnfe:
            print("module not found: {}".format(full_name))
            capture_exception(mnfe)
        except Exception as general_exception:
            print(general_exception)
            capture_exception(general_exception)
    return results


if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument("package")
    args = parser.parse_args()

    package_to_load = args.package
    results = import_submodules(package_to_load)
    for r in results:
        print(str(r))

Эти новые строки кода импортируют Sentry Python SDK и os библиотека (для чтения системных переменных окружения). Затем приложение инициализирует Sentry SDK строкой из SENTRY_DSN переменная окружения. В функции import_submodules мы затем вызовите функцию SDK capture_exception всякий раз, когда Возникает ModuleNotFoundException или другое исключение, которое попадать в более широкий сегмент Exception .

Теперь, когда наш код готов, давайте протестируем новую интеграцию с Sentry.

Тестирование скрипта и просмотр исключений

Самый простой способ проверить, работает ли код Sentry или нет, – это чтобы попытаться импортировать несуществующий модуль. Допустим, вы делаете опечатку в вашей команде и попробуйте вместо этого запустить сценарий на importliba из importlib (может быть, потому что вы используете ужасную “бабочку” Macbook Pro клавиатура вместо прочной). Попробуйте и посмотрите, что произойдет:

python module_loader.py importliba

Скрипт запустится и завершится, но будут ошибки, потому что модуль не существует. Благодаря нашему новому коду мы можем просматривать ошибки в Sentry.

Проверьте панель управления Sentry, чтобы увидеть ошибку.

Просмотр первого исключения на панели инструментов Sentry.

Мы также можем нажать на ошибку, чтобы узнать больше о том, что произошло.

Сведения об исключении на панели управления Sentry.

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

Исключение по электронной почте.

Когда все это настроено, у нас теперь есть отличная база для расширения скрипта. и улучшите обработку ошибок с помощью Sentry в качестве нашего приложения Python становится более сложным.

Что дальше?

Мы только что создали пример сценария, который выводит все модули и подмодули в пакете, а затем добавил в него Sentry, чтобы он сообщал любые исключения назад к нашему центральному размещенному экземпляру.

Это просто введение в Sentry, так что теперь вам нужно прочтите одну из следующих статей, чтобы узнать о нем больше:

Вы также можете получить представление о том, что кодировать дальше в своем проекте Python, чтение Страница полного содержания Python.

Вопросов? Свяжитесь со мной через Twitter @fullstackpython или @mattmakai . Я также на GitHub с имя пользователя mattmakai .

Что-то не так с этим сообщением? Вилка исходный код этой страницы на GitHub и отправьте запрос на перенос.