Расширение Python Logger для рассылки исключений по почте

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

Благодаря высокой конфигурируемости, мы можем расширить функциональность существующих логгеров, используя пользовательские Handlers.

Мы попытаемся расширить функциональность существующего регистратора для e-mail исключений, возникающих во время выполнения кода. Создадим python Logger, используя встроенную библиотеку logging.

import logging
logger: logging.Logger = logging.getLogger()
Вход в полноэкранный режим Выйти из полноэкранного режима

Приведенный выше код вернет экземпляр root логгера, поскольку мы не указываем никакого имени в явном виде. Если вам нужен именованный экземпляр logger, вы можете передать name в функцию getLogger.

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

Базовый класс logging.Handler предоставляет несколько крючков, которые вы можете переопределить. Для наших целей мы переопределим хук emit.

import logging
class MailHandler(logging.Handler):

    def emit(self, record: logging.LogRecord) -> None:
        if record.exc_info:
            exception = "".join(traceback.format_exception(*record.exc_info))
        else:
            exception = "".join(traceback.format_exception(*sys.exc_info()))
        self._send_mail(exception)
Вход в полноэкранный режим Выход из полноэкранного режима

Мы добавили набор операторов для перехвата исключения из записи и создания форматированной stack trace.

Хук emit будет получать logging.LogRecord, который будет содержать все подробности о записи, такие как сообщение, метка времени, номер строки, информация об исключении и т.д. Мы также добавили метод экземпляра _send_mail для отправки отформатированной трассировки стека пользователю.

Теперь давайте доработаем функцию _send_mail. Для отправки электронной почты мы будем использовать AWS SES. Вы также можете использовать smtp в качестве альтернативы.

import boto3

class MailHandler(logging.Handler):

    def _send_mail(self, message):
        self.client = boto3.client('ses')
        ses_arn = os.getenv('SES_ARN')
        source = os.getenv('SES_SOURCE')
        html = f"""
        <p>Exception occurred while execution. Please check. </p>
        <pre>{message}</pre>
        """
        self.client.send_email(
            Source=source,
            Destination={
                'ToAddresses': [
                    'foobar@gmail.com',
                ],
            },
            Message={
                'Subject': {
                    'Data': 'Exception occurred while executing Lambda. Please check.',
                },
                'Body': {
                    'Html': {
                        'Data': html
                    }
                }
            },
            SourceArn=ses_arn
        )
Вход в полноэкранный режим Выход из полноэкранного режима

Мы используем библиотеку boto3 для подключения к SES и отправки электронной почты.

Я читаю ses_arn и source из переменных окружения. Они понадобятся для отправки электронной почты на адрес назначения с помощью настроенной записи SES.

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

import logging
logger: logging.Logger = logging.getLogger()
handler = MailHandler()
handler.setLevel(logging.ERROR)
logger.logger_.addHandler(handler)
Вход в полноэкранный режим Выход из полноэкранного режима

Мы зарегистрировали наш пользовательский обработчик в экземпляре регистратора. Он будет активирован только при типе записи error, поскольку мы установили уровень logging.ERROR. Теперь вы можете протестировать ваш пользовательский обработчик, как показано ниже.

logger.error(Exception("Fake Exception"))
Вход в полноэкранный режим Выйдите из полноэкранного режима

Вы должны получить письмо с exception и трассировкой стека.

Ниже приведен полный код пользовательского обработчика.

import boto3
import logging

class MailHandler(logging.Handler):
    def emit(self, record: logging.LogRecord) -> None:
        if record.exc_info:
            exception = "".join(traceback.format_exception(*record.exc_info))
        else:
            exception = "".join(traceback.format_exception(*sys.exc_info()))
        self._send_mail(exception)

    def _send_mail(self, message):
        self.client = boto3.client('ses')
        ses_arn = os.getenv('SES_ARN')
        source = os.getenv('SES_SOURCE')
        html = f"""
        <p>Exception occurred while execution. Please check. </p>
        <pre>{message}</pre>
        """
        self.client.send_email(
            Source=source,
            Destination={
                'ToAddresses': [
                    'foobar@gmail.com',
                ],
            },
            Message={
                'Subject': {
                    'Data': 'Exception occurred while executing Lambda. Please check.',
                },
                'Body': {
                    'Html': {
                        'Data': html
                    }
                }
            },
            SourceArn=ses_arn
        )

logger: logging.Logger = logging.getLogger()
handler = MailHandler()
handler.setLevel(logging.ERROR)
logger.logger_.addHandler(handler)

#raising exception
try:
    raise Exception("Fake Exception")
except Exception as e:
    logger.error(e, exc_info=True)
Вход в полноэкранный режим Выход из полноэкранного режима

Теперь вы можете настроить обработчики логов в соответствии с вашими требованиями.

Счастливого ведения журнала 😊.

Оцените статью
devanswers.ru
Добавить комментарий