В этой статье мы познакомимся с возможностями, которые язык Python предоставляет разработчику в части логирования. Для этих целей в Python есть отдельный модуль logging, в котором есть множество полезных функций, позволяющих логировать сообщения вашего Python-скрипта на экран консоли или в отдельный файл.
Для начала работы нам потребуется подключить с помощью оператора import модуль логирования. Также в рамках нашего тестового примера мы будем использовать модуль datetime, который поставляет полезные функции для работы с датой и временем. В начале вашего скрипта/модуля (у меня это основной тестовый скрипт с именем main.py) поместите следующие строки:
import logging
import datetime
Дальше мы напишем с вами небольшую программу, которая будет демонстрировать основные возможности модуля logging по выводу различных сообщений в файл лога. Всё, что будет делать наша программа, - это запрашивать ввод данных с клавиатуры, а именно ввод целого числа в диапазоне от нуля до ста. Также мы будем проверять, что введённое пользователем значение попадает в допустимый диапазон чисел и, в зависимости от того, что было введено с клавиатуры, логировать сообщения в файл с разными уровнями логирования. Для того, чтобы пользователь также имел возможность понять, как реагирует программа, в зависимости от введённых данных мы будем выводить релевантные сообщения о результате работы программы.
Итак, в основной части скрипта пишем следующий код (я специально снабдил его комментариями, поясняющими подробно происходящее, чтобы сделать логику скрипта максимально доступной читателю с разным уровнем владения Python):
if __name__ == '__main__':
# получаем логгер для нашего модуля
logger = logging.getLogger(__name__)
# создаём хендлер для файла лога, кодировка файла будет UTF-8 для поддержки кириллических сообщений в логе
fileHandler = logging.FileHandler(filename='application_log.log', encoding='utf-8')
# задаём базовую конфигурацию логирования
logging.basicConfig(format='[%(levelname)-10s] %(asctime)-25s - %(message)s', handlers=[fileHandler], level=logging.INFO)
# кортеж, содержащий дни недели. необходим для отражения дня, в который была запущена и завершилась программа
days = ('понедельник', 'вторник', 'среда', 'четверг', 'пятница', 'суббота', 'воскресенье')
# получаем текущие дату и время в переменную now_dt
now_dt = datetime.datetime.now()
# получить точное наименование дня недели, в который запустился наш скрипт
current_weekday = days[now_dt.weekday()]
# формат для даты запуска/завершения скрипта
date_format = "%d.%m.%Y %H:%M:%S.%f"
# =======================================
# Начало основной логики скрипта
# =======================================
logging.info(">>> Программа запущена. День: %s; Точная дата и время запуска: %s", current_weekday, now_dt.strftime(date_format))
# Запрашиваем ввод числа с клавиатуры
user_input = input("Введите целое число от 0 до 100: ")
# Проверяем, что было введено число, а не что-то иное
if user_input.isnumeric():
logger.info("Пользовательский ввод корректен. Введено число: %s", user_input)
# преобразуем число, хранящееся в строке к настоящему число с типом int
input_int_var = int(user_input)
# проверяем диапазон, в котором находится число
if input_int_var < 0 or input_int_var > 100:
logger.error("Ошибка: введено некорректное число, выходящее за допустимый диапазон: %s", input_int_var)
print(f'Ошибка, введено число, выходящее за допустимый диапазон.')
elif input_int_var == 0:
logger.warning("Предупреждение: пользователь ввёл 0")
print(f'Вы ввели {input_int_var}. Ввод нуля допустим, но нежелателен, '
'т.к. операция деления на 0 число не будет выполнена.')
else:
print(f'Вы ввели {input_int_var}. Отличное число!')
result = 100 / input_int_var
print(f'Результат деления 100 / {input_int_var} = { 100 / input_int_var}')
else:
# Если пользователь ввёл не число, то логируем ошибку в файл лога и выводим ошибку на консоль
logger.error("Ошибка ввода: пользователь ввёл нечисловое значение!")
print(f'Извините, ввод \'{user_input}\' не является корректным числом в допустимом диапазоне')
# повторно получим текущие дату и время, чтобы отразить момент завершения скрипта в логе
now_dt = datetime.datetime.now()
# логируем запись о завершении программы
logging.info("<<< Программа завершена. День %s; Точная дата и время завершения: %s", current_weekday, now_dt.strftime(date_format))
По комментариям должен быть понятен основной ход выполнения скрипта. Из основных особенностей - в самом начале скрипта мы получаем экземпляр логгера для нашего скрипта в переменную logger. Этот экземпляр логгера будет использоваться для вывода сообщений в заданный нами файл лога с именем application_log.log. Файл (а точнее его хендлер) мы специфицируем посредством создания экземпляра класса FileHandler и передачей ему необходимых параметров с именем файла, а также кодировкой файла UTF-8.
Далее важной строкой является вызов функции basicConfig(), в которую мы передаём параметры, включающие в себя: формат сообщений лога, список хендлеров, которые будут использоваться для записи сообщений лога (в нашем случае у нас только один хендлер для файла лога), а также уровень логирования по умолчанию. Уровень логирования мы установим равным logging.INFO - для того, чтобы информационные сообщения также попадали в лог, а не только предупреждения/ошибки:
logging.basicConfig(format='[%(levelname)-10s] %(asctime)-25s - %(message)s', handlers=[fileHandler], level=logging.INFO)
Отмечу, что по умолчанию basicConfig() будет работать в режиме добавления в файл лога, это значит, что файл лога будет постоянно расти при каждом запуске скрипта. Если вы хотите, чтобы сообщения перезаписывались при каждом новом запуске скрипта, передайте в функцию параметр filemode='w+'. В нашем же случае filemode не задан явно, поэтому по умолчанию он будет равен значению 'a' (от англ. append - добавлять).
В части параметра format ещё отмечу входящие в него опции:
- levelname - собственно, задаёт уровень логирования текущего сообщения, попадающего в лог
- asctime - будет содержать дату и время попадания записи в лог
- message - само сообщение, которое задаётся логикой нашей программы
Также под каждый из этих элементов лога отводится фиксированное количество позиций (для уровня логирования 10 символов, для даты и времени - 25 символов) - чтобы наш лог смотрелся красиво. Значения 10 и 25 подобраны мной экспериментально, при желании вы можете их поменять.
Дальше наша программа проверяет, что ввёл пользователь и выводит разные сообщения на экран консоли и в сам файл лога.
Давайте запустим наш скрипт и введём сначала корректное значение 77, входящее в диапазон от 0 до 100:
Мы видим, что в этом случае программа отработала с успехом и выдала результат деления 100 на 77, также обратим внимание, что создался новый файл application_log.log с сообщениями логирования нашего скрипта:
Итак, мы получили фиксацию действий нашей программы уже после завершения её работы в отдельном файле лога. Давайте теперь ещё раз запустим скрипт, введём на этот раз значение 0 и посмотрим, что произойдет:
Как видим, в этот раз мы попали в другую развилку нашего скрипта, где мы проверяем, что пользователь ввёл 0 (а деление на 0 невозможно), поэтому программа завершается без выполнения деления 100 на 0. В нашем файле лога на этот раз мы увидим прибавление новых записей, появившихся после текущего текущего запуска:
Обращаем внимание, что в логе появилась строчка с уровнем логирования WARNING. При анализе файла лога она позволит нам потом быстрее найти те строки и запуски скрипта, где производилось предупреждение в логике работы скрипта.
Наконец, производим последний запуск скрипта, и в этот раз мы введём вообще не число, а строку "проверка микрофона":
Смотрим, что появилось в файле лога:
На этот раз видим, что в файле лога появилось сообщение об ошибке и новый уровень логирования ERROR. Он также будет помогать быстро находить дату и время запуска скрипта, когда пользователь вводил некорректные данные.
На этом пока всё. Я надеюсь, что статья будет полезна всем разработчикам Python, которые пожелают подключить подобные возможности логирования в файлы в своих скриптах. Попробуйте использовать модуль logging в своих скриптах для задач логирования работы ваших программ. Напишите в комментариях под статьей, знали ли вы о данном модуле? Используете ли этот модуль в своих скриптах? Насколько он, на ваш взгляд, полезен и с какими параметрами предпочитаете его использовать вы?
Спасибо за внимание, до встречи в следующих статьях!
Примеры из этой статьи: https://github.com/AllineedRu/PythonExamples/blob/main/ain_logging/__init__.py