Логирование в Python это просто

При росте проекта использовать функции print для сообщения о запуске Python-скрипта или его падение уже становится неудобно. В этом случае используйте логирование (logging). В этой статье мы расскажем, как лучше всего создавать логи в Python, когда их стоит применять, а также покажем основы библиотеки logging.

Что такое логирование и какими должны быть логи

Логирование — это способ записи информации о состоянии программы. Логами называют сами записи. Логи должны быть описательными, контекстными, реактивными [1]. Следовательно, они описывают, что произошло; они предоставляют информацию о текущем состоянии в момент открытия лога; они позволяют узнать, какие действия нужно предпринять, если это требуется. Поэтому в логах рекомендуется писать только подобную информацию, иначе рискуете создать только шум, запутав тем самым себя и коллег.

Например, лог вроде: “operation connect failed” — это шум. В нем есть описание, но нет контекста (ведь не ясно какое именно соединение и в какой момент произошла авария), а также нет информации о действии, которые нужно предпринять. Можно пойти ещё дальше и сделать лог таким: “An error happened”.

Когда создавать лог

Логи должны быть ясными и легкими для чтения. Пользователь может пропускать некоторые строчки, так как они понятны, и сосредотачивать внимание на необходимых ему аспектах. Например, логи оркестратора Apache Airflow информативны и объёмны, но вам быть может нужен только вывод, значения настроек конфигурации, время выполнения или появившаяся ошибка.

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

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

Создание логов в Python

Нам повезло, ведь в Python есть стандартная библиотека logging [2]. В нем есть специальные функции, которые названы в соответствии с уровнем или серьезностью событий. Эти уровни следующие:

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

Все эти уровни можно вызывать, используя соответствующие функции, например, logging.error. Но лучше пользоваться объектом logger (логер):

import logging

logger = logging.getLogger(__name__)

def myfunc():
    ...
    logger.info("Something relevant happened")
    ...

Объект logger предоставляет интерфейс для логирования. Также есть объекты handler (обработчик), filter (фильтр), formatter (объект формата вывода). Обработчики отправляют записи логов в соответствующее место назначения, например, в стандартный поток ошибок (stderr) или в файл. Фильтры предоставляют более детальное средство для определения, какие записи логов нужно выводить, а объекты формата вывода каким образом (по какому шаблону) должны отображаться сами логи. Рассмотрим эти объекты подробнее.

Объекты logging

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

Обработчики создаются в Python следующим образом:

s_handler = logging.StreamHandler()
f_handler = logging.FileHandler('log')
s_handler.setLevel(logging.WARNING)
f_handler.setLevel(logging.ERROR)

Итак, у нас есть два обработчика: первый со статусом WARNING записывает в стандартный поток, второй со статусом ERROR в файл.

Для форматированного вида используется шаблоны сходные с теми, которые используются в языке Си: %s — это строка, %d — это целое число. Только между процентом и символом в скобках ставится имя параметра.

s_format = logging.Formatter('%(name)s - %(levelname)s - %(message)s')
f_format = logging.Formatter('%(name)s - %(asctime)s - %(message)s')
s_handler.setFormatter(s_format)
f_handler.setFormatter(f_format)

Вывод второго обработчика покажет ещё и время генерации записи лога через asctime.

Осталось только добавить обработчики к нашему объекту logger:

logger.addHandler(c_handler)
logger.addHandler(f_handler)

Теперь, когда вы вызовите функцию warning, то в стандартном выводе увидите лог заданного формата:

>>> logger.warning('This is a warning')
__main__ - WARNING - This is a warning

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

>>> logger.error('This is an error')
__main__ - ERROR - This is an error

$ cat log
2021-09-17 16:36:59,684 - __main__ - ERROR - This is an error

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

 

О грамотном создании логов и о том, как работать с Python для решения задач Data Science вы узнаете на наших образовательных курсах в лицензированном учебном центре обучения и повышения квалификации руководителей и ИТ-специалистов (менеджеров, архитекторов, инженеров, администраторов, Data Scientist’ов и аналитиков Big Data) в Москве:

  1. FUNP: Основы языка Python для анализа данных и решения задач машинного обучения
  2. DPREP: Подготовка данных для Data Mining на Python
  3. PYML: Машинное обучение на Python
Источники
  1. https://blog.guilatrova.dev/how-to-log-in-python-like-a-pro/
  2. https://docs.python.org/3/howto/logging

Добавить комментарий

Поиск по сайту