Модуль logging в Python. Логируем сообщения скрипта в файл

User Rating: 5 / 5

В этой статье мы познакомимся с возможностями, которые язык 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 в своих скриптах для задач логирования работы ваших программ. Напишите в комментариях под статьей, знали ли вы о данном модуле? Используете ли этот модуль в своих скриптах? Насколько он, на ваш взгляд, полезен и с какими параметрами предпочитаете его использовать вы?

Спасибо за внимание, до встречи в следующих статьях!

Яндекс.Метрика