Найти в Дзене
Властелин машин

Многоканальное логирование для python проекта

Логируй так, будто завтра сервер упадет, и только твои записи спасут мир. Рассмотрим, как создать логгер для проекта, который предусматривает вывод сообщений и в консоль, и в файл журнала. Реализовывать его будем в форме класса Python, но сначала рассмотрим типичные особенности в интерактивном режиме. Нашими строительными блоками будут составляющие модуля logging: Про уровни вывода сообщений и методы я рассказывал ранее. Создадим тестовый logger: Можно заметить 2 сообщения в консоли и одно - в журнале. Почему в консоль отправлен лишний вывод? Поведение по умолчанию предполагает передачу сообщений вверх по иерархии к root-логгеру, который является предком всех logger-ов и обрабатывает сообщение путем вывода на консоль. Это регулируется атрибутом propagate (который по умолчанию равен True). Соответственно, чтобы остановить передачу "наверх" надо задать: logger.propagate = False: Для вывода списка обработчиков, которые отвечают за отправку сообщений в канал, можно обратиться к свойству lo
Оглавление

Логируй так, будто завтра сервер упадет, и только твои записи спасут мир.

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

Нашими строительными блоками будут составляющие модуля logging:

  • функция getLogger для получения логгера с заданным именем;
  • классы handler-ов: FileHandler для логирования в файл, StreamHandler для вывода в консоль, которые добавляются в полученный на предыдущем этапе логгер;
  • метод логгера setLevel для задания уровня вывода сообщений;
  • методы логгера info, debug, error, warning для вывода сообщений с заданным уровнем.

Про уровни вывода сообщений и методы я рассказывал ранее. Создадим тестовый logger:

propagate

Можно заметить 2 сообщения в консоли и одно - в журнале. Почему в консоль отправлен лишний вывод?

Поведение по умолчанию предполагает передачу сообщений вверх по иерархии к root-логгеру, который является предком всех logger-ов и обрабатывает сообщение путем вывода на консоль. Это регулируется атрибутом propagate (который по умолчанию равен True). Соответственно, чтобы остановить передачу "наверх" надо задать: logger.propagate = False:

-2

разные хэндлеры

Для вывода списка обработчиков, которые отвечают за отправку сообщений в канал, можно обратиться к свойству logger.handlers:

-3

Если попытаться добавить одинаковые хэндлеры, то список не изменится:

-4

Однако при создании новых объектов (и с другими id, соответственно) система будет считать их другими и произойдет дублирование вывода, поэтому при двухкратном обращении к одному логгеру в нашем классе надо будет предусмотреть такое поведение:

-5
-6

закрытие хэндлеров

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

-7

А так (logger.handlers[:]) создаем копию списка для итерации, поэтому результат будет пустым, и новые сообщения при обращении к логгеру не появляются:

-8

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

-9

класс логгера

На основании описанного выше можно создать класс логгера, который одновременно выводит и в журнал, и на консоль. Также добавим удобный формат сообщений (подробнее было тут).

Для демонстрационных целей имитируем наличие проекта и класс logger-а в отдельном модуле:

-10
-11
-12

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

Теперь создадим 2 одинаковых скрипта, использующих логгер:

-13

Запустим оба скрипта и увидим, что сообщения корректно выводятся и в журнал и на консоль с указанием файла-источника, что удобно для отладки:

-14

нет дублирования при многократном обращении к логгеру

Так как мы предусмотрели проверку наличия обработчиков (в блоке - if len...), при обращении к одному и тому же логгеру дважды сообщения не дублируются:

-15
-16
-17

Следует отметить, что вместо проверки наличия в логгере обработчиков, можно было бы задать их на уровне переменных класса (а не экземпляра), тогда они бы при повторном добавлении id не меняли и, соответственно, каналы бы не дублировались.

параллельная работа с несколькими логгерами

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

-18
-19
-20
-21

Ввиду ограниченных возможностей платформы, код здесь не разместить, для его копирования переходите в мой teletype.in в описании канала.

-22