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

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

Вы никогда не должны пропустить аварию

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

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

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

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

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

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

Сообщайте о сбоях программного обеспечения, используя ведение журнала.

Многие люди знают модуль журналирования Python, но меньшее количество людей знают, что он может записывать не только сообщения, но и обратную трассировку. И еще меньше людей знают, что его можно настроить с помощью набора очень гибких и мощных обработчиков сообщений, которые смогут отправлять зарегистрированные сообщения повсюду. От наиболее очевидного случая отправки этих сообщений в файлы журналов до использования протокола SysLog, отправки их по сети или даже по электронной почте.

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

import logging
import logging.handlers

crashlogger = logging.getLogger('__crashes__')

def configure_crashreport(mailhost, fromaddr, toaddrs, subject, 
                          credentials, tls=False):
        crashlogger.addHandler(
            logging.handlers.SMTPHandler(
                mailhost=mailhost,
                fromaddr=fromaddr,
                toaddrs=toaddrs,
                subject=subject,
                credentials=credentials,
                secure=tuple() if tls else None
            )
        )

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

configure_crashreport(
        'your-smtp-host.com',
        '[email protected]',
        '[email protected]',
        'Automatic Crash Report from TestApp',
        ('smtpserver_username', 'smtpserver_password'),
        tls=True
)

После того, как ведение журнала настроено правильно, каждый раз, когда мы регистрируем что-либо через crashlogger, мы будем получать это по электронной почте.

crashlogger.warn("Hey, this is a message sent by email!")

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

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

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

В случае веб-приложения эта функция, вероятно, будет той, которая обслуживает каждый запрос.

import functools
def crashreport(f):
    @functools.wraps(f)
    def _crashreport(*args, **kwargs):
        try:
            return f(*args, **kwargs)
        except Exception as e:
            crashlogger.exception(
                '{} crashed\n'.format(f.__name__)
            )
            raise
    return _crashreport

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

@crashreport
def main():
    3 / 0

При вызове main программа выйдет из строя с ошибкой ZeroDivisionError, и если мы вызовем configure_crashreport, мы получим сбой по электронной почте.

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

Веб-приложения и WSGI

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

@crashreport
def hello_world(environ, start_response):
    start_response('200 OK', [('Content-Type', 'text/html')])
    return ['''Hello World''']
from wsgiref.simple_server import make_server
srv = make_server('localhost', 8080, hello_world)
srv.serve_forever()

Существуют решения для любой веб-инфраструктуры, совместимой с WSGI, которые предоставляют промежуточное ПО, которое улавливает любой сбой приложения.

Одним из таких решений является проект Backlash из веб-фреймворка TurboGears2, который может использоваться с любым другим фреймворком и не зависит от самого TurboGears.

Вы можете заключить свое приложение в TraceErrorsMiddleware и получать уведомления с помощью любого настроенного вами репортера (для получения электронных писем мы можем использовать EmailReporter):

from backlash.trace_errors import EmailReporter
app = backlash.TraceErrorsMiddleware(
    hello_world,       
    [EmailReporter(
        smtp_server='your-smtp-host.com',
        from_address='[email protected]',
        error_email='[email protected]',
        smtp_username='smtpserver_username',
        smtp_password='smtpserver_password',
        smtp_use_tls=True,
        error_subject_prefix='Automatic Crash Report from TestApp'
    )]
)

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

Такие инструменты, как Backlash, также могут включать дополнительные функции, такие как отслеживание медленных запросов или отладка живого приложения, независимо от вашего пути: через стандартную библиотеку или с помощью стороннего инструмента вы действительно должны отслеживать свои сбои в производственной среде и следить за тем, чтобы ваши пользователи не сталкиваются с ошибками и сбоями, о которых вы не подозреваете!

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

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