Ведение журналов — важнейшая часть любого приложения или процесса, который вы создаете, поскольку это помогает вам отлаживать или отслеживать происходящее. Логирование — это очень обширная тема сама по себе, но она очень полезна, когда вы хотите выполнить некоторые побочные эффекты, перенаправить вывод на другие сервисы или выполнить некоторые побочные вычисления и т.д.
Благодаря высокой конфигурируемости, мы можем расширить функциональность существующих логгеров, используя пользовательские 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)
Теперь вы можете настроить обработчики логов в соответствии с вашими требованиями.
Счастливого ведения журнала 😊.