Недавно мы разбирали, что такое логгеры, и подключали их к проектам на JavaScript. Смысл в том, чтобы научить наши программы отчитываться о своей работе, что упростит нам отладку.
Вот короткая версия того, что было в предыдущих частях:
- логгер — это специальный модуль или библиотека, которая фиксирует определённые события в нашей программе;
- на что реагировать и как фиксировать — решает программист;
- для этого он пишет специальную команду типа
лог.добавить(‘Запускаем главный модуль’)
илилог.ошибка(‘Файл не найден’)
; - сам по себе лог ничего не исправляет — он только сообщает о том, что происходит в программе;
- есть несколько основных уровней логирования — ошибки, предупреждения, информационные сообщения, сообщения отладки и разовые события;
- эти уровни нужны для того, чтобы фильтровать сообщения по степени важности, например посмотреть только сообщения об ошибках или о штатных срабатываниях;
- лог можно выводить в консоль, в файл или передавать с помощью сокетов или API в другую программу;
- когда мы подключили логгер в наш проект с арканоидом на JavaScript, то неожиданно для себя нашли ошибку, которую без логгера мы не замечали.
Теперь поработаем с логгером в проекте на Python и посмотрим, найдём ли мы ещё ошибки в своём коде.
Настраиваем логгер
Python хорош тем, что с ним в комплекте идёт много полезных библиотек, в том числе логгер. Поэтому мы будем использовать встроенный логгер, который подключается так:
import logging
С этого момента мы можем использовать логгер в любом месте, просто указав нужный уровень сообщения, например:
logging.debug("Это сообщение для отладки программы")
logging.info("Информационное сообщение")
logging.warning('Предупреждение — нужно проверить, всё ли в порядке')
logging.error("Ошибка! Что-то пошло не так")
Если запустить этот код, то мы увидим только последние два сообщения:
Дело в том, что логгер по умолчанию выводит только сообщения с уровнем warning и выше. Чтобы вывести все сообщения начиная с самого нижнего уровня debug, используем такую команду:
logging.basicConfig(level=logging.DEBUG)
Теперь все сообщения на месте:
Выводим сообщения в файл
По умолчанию логгер пишет всё в консоль, но можно отправлять все сообщения в файл. Для этого в настройках логгера укажем имя файла:
logging.basicConfig(level=logging.DEBUG, filename=”thecode.log”)
Файл появится в той же папке, что и программа, и мы сможем открыть его в любом текстовом редакторе:
Но при этом в консоли мы ничего не увидим — весь вывод ушёл в файл. Чтобы оставить сообщения в консоли и при этом добавлять их в файл, сделаем немного программистского колдунства — вручную настроим потоки вывода и объединим их в одно целое:
# добавляем поток вывода в файл
file_log = logging.FileHandler("thecode.log")
# и вывод в консоль
console_out = logging.StreamHandler()
# указываем эти два потока в настройках логгера
logging.basicConfig(handlers=(file_log, console_out), level=logging.DEBUG)
Теперь лог будет выводиться и в файл, и в консоль.
Добавляем логгер в проект
Смысл логгера — выводить сообщения в тех местах, где программисту нужна какая-то информация о том, как работает программа в этом месте. Нет смысла запихивать логгер после каждой строчки кода — получится огромный лог, в котором будет сложно разобраться.
Чтобы поработать с логгером на практике, достаточно любого, даже самого простого проекта на Python. Мы возьмём лёгкую задачу про безумного рекрутера на Python и добавим в неё лог — так будет проще понять, как всё работает. Если освоитесь с этим, то сможете подключить логгер в проект любой сложности — принцип будет точно такой же.
Мы добавим несколько логов
- информационные — на создание переменных;
- предупреждения — перед входом в каждый цикл (если мы зациклимся, то сразу будем знать, где именно);
- отладки — промежуточные значения переменной цикла.
Вот готовый код:
# подключаем логгер
import logging
# добавляем поток вывода в файл
file_log = logging.FileHandler("thecode.log")
# и вывод в консоль
console_out = logging.StreamHandler()
# указываем эти два потока в настройках логгера
logging.basicConfig(handlers=(file_log, console_out), level=logging.DEBUG)
logging.info("Объявляем переменные")
# Сколько джуниоров было на старте в старом офисе
junior = 1
# Сколько дней ушло на заполнение старого офиса
day = 30
# Сделаем диапазон для цикла
month = range(1,day)
# Каждый день в старом офисе…
logging.warning("Входим в цикл удваивания старых джуниоров")
for current_day in month:
# …удваивалось количество джуниоров
junior = junior * 2
logging.debug("Количество старых джуниоров: " + str(junior))
# В новом офисе у нас уже два джуниора на старте
new_junior = 2
# Заведём переменную для подсчёта дней в новом офисе
new_day = 0
# Пока в новом офисе не станет столько же людей, как и в новом…
logging.warning("Входим в цикл удваивания новых джуниоров")
while new_junior <= junior:
# …мы удваиваем количество джуниоров в новом офисе…
new_junior = new_junior *2
logging.debug("Количество новых джуниоров: " + str(new_junior))
# …и считаем каждый прошедший день
new_day = new_day + 1
# Выводим количество дней, через сколько мы заполним новый офис
print(new_day)
Когда мы запустим этот код, то увидим очень много сообщений в консоли. Это произошло потому, что мы выводим логи всех уровней — от отладочных до критических:
Так произошло из-за того, что мы указали уровень DEBUG в настройках логгера. Изменим их так, чтобы логгер нам их не показывал, а всё другое — оставил:
logging.basicConfig(handlers=(file_log, console_out), level=logging.INFO)
Вот так, управляя уровнем вывода логов, можно как детально погрузиться в ход работы программы, так и посмотреть всю работу без подробностей.