Найти тему
Computer Pro

Основы дебаггинга и профилирования. Часть 1

Оглавление

Движемся дальше по курсу Python Advanced, вот вроде бы быстро прослушал все лекции по данной теме, а в конечном итоге - в голове задержалось не так уж и много. Может быть еще наложилось на то что у лектора не очень внятный голос, поэтому постараюсь данной статьёй структурировать пройденный материал (прежде всего в своей голове). И затем только перейти к "Практической работе". Быть может так будет более продуктивно?! Не знаю... Поглядим далее...

Обработка ошибок в Flask. Flask Error Handlers

Напишу простой эндпойнт (endpoint) который через FlaskForm принимает два числа, извлекает их из формы и возвращает пользователю результат их деления. Либо ошибку:

забыл убрать print... из кода, ну да ладно...
забыл убрать print... из кода, ну да ладно...

при отправке запроса получаем ответ:

-3

Казалось бы, все работает, одно делит другое, но если попытаться поделить переменную "а" на 0:

Как можно видеть ошибка 500, когда сервер не понимает как ему реагировать он выдает ошибку 500 Internal Server Error
Как можно видеть ошибка 500, когда сервер не понимает как ему реагировать он выдает ошибку 500 Internal Server Error

Далее идет непереводимая игра умными словами, которая заканчивается примерно так:

Тут же выскакивает куча гневных записей и какие-то ошибки
Тут же выскакивает куча гневных записей и какие-то ошибки

Наш сервер, пока не знает что делать с ошибкой деления на 0, вот он и выдал ошибку 500. Это происходит при любом необработанном исключении и Flask отправляет их наружу, пользователю. Если бы мы пользовались браузером, то все эти "ругательства" увидели бы в браузере.

Чтобы исключить отправку исключения (как в верхнем случае ZeroDivisionError) сделаем обработчик, который принимает данное исключение:

обработчик
обработчик
-7

И теперь если снова попытаться поделить на 0, то сервер нам вернет не кучу непонятных слов, а внятное описание ошибки:

-8

Такая запись хороша когда мы точно знаем тип ошибки а не абстрактное Internal Server Error, где может быть не только "деление на ноль" но и другие виды ошибок, как же поступить в этом случае? Опять жеж ошибку нужно не только вывести пользователю, но и сохранить в файл, дабы когда пожалуется пользователь на выпавшую ему ошибку мы могли посмотреть логи и узнать из-за чего она возникла.

Для демонстрации этого создадим новую программу banki.py, которая обрабатывает такой эндпойнт: http://127.0.0.1:5000/bank_api/1/3

Ответом на обработку такого эндпойнта (считать файл с id = 1, вывести пользователя с id = 3) должно быть ФИО пользователя, хранящееся в файле CSV:

-9

Идеальный запрос, идеальный ответ (когда мы точно знаем что такой файл есть, и такая запись в файле так же присутствует):

-10

И если в файле нет пользователя с указанным id:

-11

Код данного эндпойнта:

-12

Это все работает для идеальных условий. Но бывает так что либо соединение с сервером очень медленное и и файл был не прочитан, либо вообще такого файла нет, то пользователю выдается ошибка в виде - "много букв"! У пользователя паника и он не знает что делать:

-13

Для этого мы и будем делать обработчик этого "Internal Server Error". То есть, если возникает ошибка 500 (Internal Server Error), то пользователю будет так и писаться (Internal Server Error), без всякой дополнительной информации:

-14

А на сервере будет сохраняться log-файл с детальной информацией об ошибке:

-15

Ну и полная версия программы с обработчиком ошибки Internal Server Error:

-16
-17

Иерархия исключений Python. Логирование. Модуль logging

В примере выше, в обработчике ошибок можно было видеть что сначала обрабатывается ошибка FileNotFounError а затем OSError. Такой порядок - не с проста! Ибо у ошибок существует своя иерархия:

-18

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

-19

Чтобы логи были более понятные, для этого существует модуль logging. Напишем еще одну программу input_and_check_password.py

-20

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

Последний пароль я ввел правильно, иначе был бы exit code = 1
Последний пароль я ввел правильно, иначе был бы exit code = 1

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

-22

Теперь нужно подключить logging и создать объект logger:

-23

Итак, я свои "принты" заменил сообщения от logger'a, с соответствующим уровнем важности. Об уровнях важности:

-24

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

-25
-26

Так же с помощью логгера исправим нашу программу divide.py (сервер деления чисел):

-27
-28

Посмотрим какие будут сообщения выводиться в консоли при обработке данных сервером:

-29

В нашем хендлере сработало исключение и передало его в консоль

-30

Логгирование в файл

Для демонстрации логгирования событий в файл мы модернизируем нашу программу bank.py. Вместо "принтов" там будут логи.

-31
-32

Для того чтобы логгирование записывалось в файл нужно обратиться к документации модуля logging и найти нужное ключевое слово, которое вставим в качестве аргумента в logging.basicConfir(**kwargs)

Ключевые слова базовой настройки логгирования модуля logging:

-33
из официальной документации logging
из официальной документации logging

Добавляем второй аргумент к имеющемуся level=logging.DEBUG:

-35

Перезапускаю сервер и что я вижу, в консоли больше нет оповещений, все сообщения от сервера пишутся в файл:

-36

Я еще не делал никаких запросов, пробую запрос...

-37
-38

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

-39

И все изменения в файле banking.log будут выводиться в терминале в режиме реального времени.

Итак, логи у нас пишутся в файл, но когда это сообщение пришло - непонятно. Надо ввести в basicConfig еще один аргумент - format:

-40

Добавляем аргумент format в настройки логгера:

-41

И теперь наши логи идут более читабельно по времени.

-42

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

-43