Движемся дальше по курсу Python Advanced, вот вроде бы быстро прослушал все лекции по данной теме, а в конечном итоге - в голове задержалось не так уж и много. Может быть еще наложилось на то что у лектора не очень внятный голос, поэтому постараюсь данной статьёй структурировать пройденный материал (прежде всего в своей голове). И затем только перейти к "Практической работе". Быть может так будет более продуктивно?! Не знаю... Поглядим далее...
Обработка ошибок в Flask. Flask Error Handlers
Напишу простой эндпойнт (endpoint) который через FlaskForm принимает два числа, извлекает их из формы и возвращает пользователю результат их деления. Либо ошибку:
при отправке запроса получаем ответ:
Казалось бы, все работает, одно делит другое, но если попытаться поделить переменную "а" на 0:
Далее идет непереводимая игра умными словами, которая заканчивается примерно так:
Наш сервер, пока не знает что делать с ошибкой деления на 0, вот он и выдал ошибку 500. Это происходит при любом необработанном исключении и Flask отправляет их наружу, пользователю. Если бы мы пользовались браузером, то все эти "ругательства" увидели бы в браузере.
Чтобы исключить отправку исключения (как в верхнем случае ZeroDivisionError) сделаем обработчик, который принимает данное исключение:
И теперь если снова попытаться поделить на 0, то сервер нам вернет не кучу непонятных слов, а внятное описание ошибки:
Такая запись хороша когда мы точно знаем тип ошибки а не абстрактное Internal Server Error, где может быть не только "деление на ноль" но и другие виды ошибок, как же поступить в этом случае? Опять жеж ошибку нужно не только вывести пользователю, но и сохранить в файл, дабы когда пожалуется пользователь на выпавшую ему ошибку мы могли посмотреть логи и узнать из-за чего она возникла.
Для демонстрации этого создадим новую программу banki.py, которая обрабатывает такой эндпойнт: http://127.0.0.1:5000/bank_api/1/3
Ответом на обработку такого эндпойнта (считать файл с id = 1, вывести пользователя с id = 3) должно быть ФИО пользователя, хранящееся в файле CSV:
Идеальный запрос, идеальный ответ (когда мы точно знаем что такой файл есть, и такая запись в файле так же присутствует):
И если в файле нет пользователя с указанным id:
Код данного эндпойнта:
Это все работает для идеальных условий. Но бывает так что либо соединение с сервером очень медленное и и файл был не прочитан, либо вообще такого файла нет, то пользователю выдается ошибка в виде - "много букв"! У пользователя паника и он не знает что делать:
Для этого мы и будем делать обработчик этого "Internal Server Error". То есть, если возникает ошибка 500 (Internal Server Error), то пользователю будет так и писаться (Internal Server Error), без всякой дополнительной информации:
А на сервере будет сохраняться log-файл с детальной информацией об ошибке:
Ну и полная версия программы с обработчиком ошибки Internal Server Error:
Иерархия исключений Python. Логирование. Модуль logging
В примере выше, в обработчике ошибок можно было видеть что сначала обрабатывается ошибка FileNotFounError а затем OSError. Такой порядок - не с проста! Ибо у ошибок существует своя иерархия:
В прошлом примере мы сделали лог ошибок, но он так же не сильно дает понятие ни по времени, ни по месту в программе где эта ошибка случилась...
Чтобы логи были более понятные, для этого существует модуль logging. Напишем еще одну программу input_and_check_password.py
Нельзя просто так вписать в хеш пароля первое что придет в голову чтобы пароль совпал, нужно создать этот хеш. Под комментами этот креатор. Почему-то не отключается эхо пароля, то есть вводя пароль - видим все символы и терминал предупреждает об этом:
Хотя в чистой консоли программа работает без предупреждений, да и вводимый пароль не отображается. Немного модернизировал принтами, дабы на каждом вводе пароля было понятно - какие ошибки делаю при вводе или ошибок нет:
Теперь нужно подключить logging и создать объект logger:
Итак, я свои "принты" заменил сообщения от logger'a, с соответствующим уровнем важности. Об уровнях важности:
Вот такой получился код, вместо "принтов" использовано логгирование, далее мы ознакомимся более детально.
Так же с помощью логгера исправим нашу программу divide.py (сервер деления чисел):
Посмотрим какие будут сообщения выводиться в консоли при обработке данных сервером:
В нашем хендлере сработало исключение и передало его в консоль
Логгирование в файл
Для демонстрации логгирования событий в файл мы модернизируем нашу программу bank.py. Вместо "принтов" там будут логи.
Для того чтобы логгирование записывалось в файл нужно обратиться к документации модуля logging и найти нужное ключевое слово, которое вставим в качестве аргумента в logging.basicConfir(**kwargs)
Ключевые слова базовой настройки логгирования модуля logging:
Добавляем второй аргумент к имеющемуся level=logging.DEBUG:
Перезапускаю сервер и что я вижу, в консоли больше нет оповещений, все сообщения от сервера пишутся в файл:
Я еще не делал никаких запросов, пробую запрос...
Но теперь возникает другая проблема, в терминале пусто, чтобы видеть логи мне нужно держать открытым файл banking.log, это не очень удобно. Но можно запустить программу командной строки tail вот с таким синтаксисом:
И все изменения в файле banking.log будут выводиться в терминале в режиме реального времени.
Итак, логи у нас пишутся в файл, но когда это сообщение пришло - непонятно. Надо ввести в basicConfig еще один аргумент - format:
Добавляем аргумент format в настройки логгера:
И теперь наши логи идут более читабельно по времени.
Ну вот, теперь у нас есть базовые знания по логгированию. Попробуем применить их на практике, выполняя домашние задания.