Как сделать прогресс бар или логирование для отслеживания процесса программы python

Обновлено: 30.06.2024

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

Когда использовать logging

Для самого простого использования, модуль предоставляет функции debug(), info(), warning(), error() и critical(). Название функций соответствует названию уровней или серьезности логируемых событий. Рекомендации по использованию стандартных уровней сведены в таблицу (в порядке возрастания серьезности).

Simple example. (или В Бой!)

Самый простой пример использования модуля выглядит так:

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

Модуль logging имеет модульный подход и предлагает несколько категорий компонентов: логгеры (loggers), обработчики (handlers), фильтры (filters) и объекты форматирования (formatters).

    содержит интерфейс для логирования. Это основной объект, именно он создает записи в лог. обработчик, который направляет созданные логгером записи в пункт назначения. Например вывод в консоль, запись в файл, отправка письма и т.д. позволяет получить больший контроль над фильтрацией записей чем стандартные уровни логирования.

Базовый класс реализует только одно поведение: выстраивание иерархии логгеров при помощи имени логгера и точки. Например инициализирован логгер с именем A.B , тогда записи логгеров с именами A.B.C , A.B.C.D , A.B.D будут обработаны, а логгеров с именами A.BB или B.B.C отброшены.

    является шаблоном для форматирования записи. Возможные атрибуты для заполнения шаблона здесь

Способы конфигурации модуля.

Это базовый, простой способ. Рассмотрим его, на примере чтобы погрузится удивительнйый мир логирования (смайлик):

Рассмотрим параметры переданные в baseConfig: format - преобразует вывод логера по переданному шаблону, filename - сообщает логгеру что необходимо логи заносить в файл с переданным названием example.log , filemod : w - перезаписывать файл при каждом запуске файла, a - дописывать в конец файла и наконец level устанавливает уровень логирования DEBUG . В результате выполнения будет создан файл filemod.log в котором окажутся записи:

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

Этот способ открывает весь набор инструментов библиотеки. Для использования необходимо создать логгер, создать и добавить обработчики, фильтры и шаблон форматирования. Да, у одного логгера может быть несколько обработчиков. И снова пример:

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

и файл с именем 3_example.log с содержимым:

Хочу пояснить следующие моменты:

Оптимизация. Процесс подстановки аргументов в шаблон ленивый, и произойдет только если запись действительно будет обрабатываться, поэтому в функцию логирования нужно передавать шаблон строки с аргументами, например logger.debug("received params %s", a) . Если же передавать заранее сформированную строку, то ресурсы системы будут потрачены независимо от того будет запись занесена в лог или нет.

Оптимизация. Вычисление аргуменов для логирования может быть долгим. Для того чтобы не терять время можно воспользоваться методом isEnabledFor логгера, который принимает уровень логирования, проверяет будет ли производится запись и возвращает ответ в True или False . Например:

Мой любимый способ. Сразу к примеру:

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

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

Дальше с помощью метода dictConfig обрабатывается словарь-конфигурация, затем берется свежесозданный логгер main и выводится список имен описанных логгеров. Вывод может быть таким:

Загружает конфигурацию из файла, файл должен быть в формате библиотеки configparser. Мехнизм описания конфигурации в файле очень похож на словарь dictConfig -а. Сама конфигурация чуть более многословна, так же предоставляет полный контроль над логгерами.

По умолчанию disable_existing_loggers = True Вместо непосредственно файла может принимать экземпляр класса RawConfigParser, что позволяет хранить конфигурацию проекта в одном файле.

Код думаю понятен, переходим к седующему способу конфигурации.

Поднимает сокет, который слушает и загружает конфигурацию. Наверное применяется в больших, распределенных проектах. Я это не использовал поэтому без примеров.

Некоторые варианты использования

Как я уже упоминал, в логгерах, через имена, реализовано наследование. Рассмотрим пример:

выполнив python 6_inh_main.py 1 2 3 в консоли получим:

Пройдем по порядку. Для начала имортируем модуль 4_dictConfig в котором инициализируем логгеры, это он в первой строке сообщает какие логгеры были настроены. Мне нравится подход, при котром логгеры инициализируюися в одном модуле, и затем получаются getLogger -ом по необходимости.

Затем имортируется модуль 6_inh_slave , и в нем, при вызове getLogger -а к имени инициализированного логгера slave через точку добавлен __name__ (вернет имя модуля если модуль импортируется), что определяет логгер-потомок slave -а с именем slave.6_inh_slave. Логгер-потомок наследует поведение логгера-родителя, однако теперь понятно из логгера какого модуля была сделана запись. Можно в наследовании подставлять имя пакета, название функционального блока или класса, здесь как душе угодно.

This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters

- это процесс записи информации в файлы журнала. Файлы журнала содержат информацию о различных событиях, которые произошли в операционной системе, программном обеспечении или при обмене данными.

Цель логирования

Логирование ведется в следующих целях:

  • Сбор информации
  • Исправление проблем
  • Генерация статистики
  • Аудит
  • Профилирование

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

Какие события регистрировать

События, которые должны регистрироваться, включают в себя ошибки проверки входа, ошибки проверки подлинности и авторизации, ошибки приложения, изменения конфигурации, а также запуск и завершение работы приложения.

Какие события не регистрировать

События, которые не должны регистрироваться, включают в себя исходный код приложения, значения идентификации сеанса, токены доступа, конфиденциальные личные данные, пароли, строки подключения к базе данных, ключи шифрования, данные банковского счета и данные владельца карты.

Лучшие практики логирования

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

Модуль logging

Модуль регистрации Python определяет функции и классы, которые реализуют гибкую систему регистрации событий для приложений и библиотек.

Компоненты модуля logging

Модуль ведения журнала имеет четыре основных компонента: средства ведения журнала (регистраторы), обработчики, фильтры и средства форматирования. Регистраторы предоставляют интерфейс, который непосредственно использует код приложения. Обработчики отправляют записи журнала (созданные регистраторами) в соответствующий пункт назначения. Фильтры предоставляют более точную возможность определить, какие записи журнала выводить. Форматировщики определяют формат записей журнала в конечном выводе.

Иерархия logging в Python

Регистраторы Python образуют иерархию. Регистратор с именем main является родителем main.new .

Уровни логирования Python

Уровни используются для определения серьезности события. Существует шесть уровней ведения журнала:

У логгеров есть концепция эффективного уровня. Если уровень явно не задан в логгере, вместо него используется уровень его родителя. Если родитель не имеет явного установленного уровня, его родитель проверяется, и так далее - все предки ищутся, пока не будет найден явно установленный уровень.

Корневой логгер

Корневой регистратор всегда имеет явный набор уровней, который по умолчанию WARNING.

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

Простой пример логирования Python

Модуль logging имеет простые методы, которые можно использовать сразу, без какой-либо настройки. Это может быть использовано для простой регистрации.

simple.py

Установка уровня логирования

set_level.py

В этом примере мы изменили уровень ведения журнала на DEBUG.

getLogger() возвращает регистратор с указанным именем. Если имя None, он возвращает корневой логгер. Имя может быть разделенной точкой точкой, определяющей иерархию журналирования; например, 'a', 'a.b' или 'a.b.c'. Обратите внимание, что существует неявное корневое имя, которое не отображается.

Эффективный уровень ведения журнала в Python

Эффективный уровень ведения журнала - это уровень, установленный в явном виде или определяемый родителями журнала.

effective_level.py

В этом примере мы рассмотрим эффективный уровень логирования двух логгеров.

Уровень dev_logger не установлен; затем используется уровень его родителя.

Обработчики журналов Python

Обработчики распространяются как уровни. Если регистратор не имеет установленного обработчика, его цепочка предков используется для поиска обработчика.

handlers.py

В примере создаются два обработчика для регистратора: обработчик файлов и обработчик консоли.

FileHandler отправляет записи журнала в файл test.log .

StreamHandler отправляет записи журнала в поток. Если поток не указан, используется sys.stderr .

Обработчик добавляется в логгер с помощью addHandler() .

Средства форматирования журнала Python

formatter.py

В этом примере создается консольный логгер и добавляется форматировщик в его обработчик.

Форматтер устанавливается на обработчик с помощью setFormatter() .

Python logging basicConfig

basicConfig() настраивает корневой логгер. Он выполняет базовую настройку системы ведения журнала, создавая обработчик потока с форматером по умолчанию. debug() , info() , warning() , error() и critical() автоматически вызывают basicConfig() , если для корневого регистратора не определены обработчики.

basic_config.py

В этом примере корневая программа регистрации настраивается с помощью basicConfig .

Python logging fileConfig

fileConfig() считывает конфигурацию ведения журнала из файла формата configparser .

log.conf

log.conf определяет регистратор, обработчик и форматер.

file_config.py

В этом примере файл конфигурации журнала считывается из файла log.conf .

Переменные в Python logging

Динамические данные записываются с использованием форматирования строки.

log_variable.py

Форматирование даты/времени в Python logging

date_time.py

Мы включаем строку datetime в журнал вместе с asctime .

Опция datefmt форматирует строку даты и времени.

Обратите внимание на разницу в формате строки даты и времени.

Трассировка стека логирования Python

Трассировка стека - это стек вызовов функций, которые выполнялись до точки возникновения исключений. Трассировка стека включается опцией exc_info .

stack_trace.py

В этом примере мы регистрируем исключение, которое выдается при попытке доступа к несуществующему списковому индексу.

Трассировка стека включается в журнал, устанавливая для exc_info значение True .

Трассировка стека включена в журнал.

Python logging getLogger

getLogger() возвращает регистратор с указанным именем. Если имя не указано, возвращается корневой регистратор. Обычная практика - помещать туда имя модуля с помощью __name__ .

Все вызовы этой функции с заданным именем возвращают один и тот же экземпляр регистратора. Это означает, что экземпляры регистратора никогда не нужно передавать между различными частями приложения.

get_logger.py

В примере создается новый регистратор с помощью getLogger() . Дается обработчик файла и форматер.

Создан регистратор с именем main; мы устанавливаем уровень ведения журнала на DEBUG.

Обработчик добавляется в логгер с помощью addHandler() .

Конфигурация YAML для логирования в Python

Подробности ведения журнала можно определить в файле конфигурации YAML. YAML - это читаемый человеком язык сериализации данных. Обычно используется для файлов конфигурации.

Нам нужно установить модуль pyyaml .

config.yaml

log_yaml.py

В этом примере мы читаем файл конфигурации и используем dev logger .

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


  • В отладке кода для определения потока исходного кода во время разработки и после развертывания.
  • Оповещении об исключительном событии, вызванном кодом. Например, нехватке памяти и т. д.
  • Поиске пользователей или систем, обращающихся к коду.

Что должно записываться в логи?

  • Информация о доступе : пользователи и устройства, имеющие доступ к коду.
  • Версия кода : текущая редакция приложения.
  • Отметка времени : отметки времени всех ключевых событий.
  • Результаты : результаты переменных, вычисленных в коде.
  • Исключения , возникающие вместе с трассировкой стека.
  • Поток кода : различные классы и функции, вызываемые во время выполнения кода.

Как реализовать логирование в Python?

Различные уровни логирования в порядке их сложности :

  1. DEBUG : используется только при диагностике проблем.
  2. INFO : только для информации, используемой для понимания потока кода при диагностике проблемы.
  3. WARNING : когда возникает непредвиденная ситуация, но код все еще выполняется.
  4. ERROR : когда код не может выполнять какую-либо функцию.
  5. CRITICAL : серьезная ошибка, когда программа не может продолжить работ.

Простой код реализации логирования различных вариантов сложности:

Как реализовать логирование в Python?

Вывод в консоль

Мы используем метод basicConfig(), чтобы установить базовую конфигурацию для системы логирования.

Вывод в консоль

Метод basicConfig() не будет учитывать новые настройки, если корневой регистратор уже настроен. Мне пришлось перезапустить ядро, чтобы приведенный выше код работал правильно. Так как при первом вызове любой из функций она настраивает корневой регистратор внутри системы.

Параметры, принимаемые basicConfig()

format : строка формата, доступную в качестве атрибутов в LogRecord .

level : уровень сложности для корневого регистратора.

Запись отформатированного лога в файл log.txt в режиме добавления с уровнем сложности DEBUG

Запись отформатированного лога в файл log.txt в режиме добавления с уровнем сложности DEBUG

Приведенный ниже фрагмент кода демонстрирует логирование в классах и функциях.

Мы создаем класс TestLog с divide(). Он принимает два параметра и возвращает результат деления. В случае ошибки в делении мы хотим иметь трассировку стека в файле лога.

Создание экземпляра класса TestLog и вызов divide().

Логирование трассировки стека

Для отображения трассировки стека нужно установить для exc_info значение true в блоке except обработки исключений.

Логирование трассировки стека

Заключение

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

Пожалуйста, оставляйте свои комментарии по текущей теме статьи. Мы крайне благодарны вам за ваши комментарии, отклики, подписки, лайки, дизлайки!

Пожалуйста, опубликуйте свои отзывы по текущей теме материала. За комментарии, дизлайки, отклики, подписки, лайки низкий вам поклон!

Favorite

Добавить в избранное

Как использовать Logging в Python 3

Введение

М одуль Logging является частью стандартной библиотеки в Python и обеспечивает отслеживание событий, которые происходят во время работы программы. Вы можете добавить протоколирование вызовов в коде, чтобы указать, какие события произошли.

Модуль Logging позволяет вести журнал диагностики, который регистрирует события, связанные с работой приложений, а также ведение журнала аудита, который регистрирует события операций пользователя для анализа. Особенно используется для записи событий в файл.

Зачем использовать модуль logging

Модуль Logging хранит записи о событиях, которые происходят в рамках программы, что позволяет увидеть результат, связанный с каким – либо из событием, которое происходит во время выполнения части программного обеспечения.

Вы можете знакомы с проверкой, когда события происходят с помощью заявления print() на протяжении всего кода. Заявление print() обеспечивает основной способ отладки кода для решения проблем. В то время как вложения заявления print() по всему коду может отслеживать поток выполнения и текущее состояние программы, это решение оказывается хуже, чем при использовании модуля Logging по нескольким причинам:

  • Становится трудно отличить вывод отладки и нормальный вывод программы, так как оба смешиваются
  • При использовании заявления print(), рассеянных по всему коду, не существует простой способ отключить те, которые обеспечивают отладку
  • Становится трудно удалить все заявления print(), когда вы закончите с отладкой
  • Там нет записей журнала, который содержит легко доступную диагностическую информацию

Это хорошая идея, получить в привычку использовать модуль logging в коде, так как это больше подходит для приложений, прорастающих за пределы простых скриптов Python и обеспечивает устойчивый подход к отладке.

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

Если вы привыкли использовать заявление print(), чтобы увидеть, что происходит в программе, вы может привыкли видеть программу, которая определяет класс и инициализирует объекты, которые выглядят примерно так:

Выше код использует метод __init__ для определения name и объект price из класса Pizza. Далее он использует два метода, один называется make() для приготовления пиццы, а также один называется eat() для употребления пиццы. Эти два метода принимают в параметре quantity, который инициализируется как 1.

Теперь давайте запустим программу:

Получим следующий результат:

В то время как заявление print() позволяет нам видеть, как работает код, мы можем использовать модуль logging вместо него.

Давайте удалим или закомментируйте заявление print() в коде, и добавим import logging в начало файла:

Модуль logging имеет уровень WARNING по умолчанию, который является уровнем выше DEBUG. Так как мы будем использовать Модуль logging для отладки в данном примере, нам нужно изменить конфигурацию таким образом, чтобы был уровень logging был DEBUG, который будет возвращать информацию на консоль для нас. Мы можем сделать это, добавив следующую строку ниже оператора импорта:

Уровень logging.DEBUG относится к постоянному целочисленному значению, мы ссылаемся в коде выше, чтобы установить порог. Уровень DEBUG10.

Теперь мы заменим все заявления print() на заявление logging.debug(). В отличие от константы logging.DEBUG которое является постоянным, logging.debug() является методом модуля logging. При работе с этим методом, мы можем использовать ту же строку, которая передается в print(), как показано ниже.

В этот момент, когда мы запускаем программу командой python pizza.py, мы получим следующий результат:

Например, вы можете установить регистратор равный регистраторам, которые имеют разные названия и другой вывод:

Для того, чтобы начать запись в файл, мы можем изменить logging.basicConfig() включив параметр filename. В этом случае, давайте назовем имя файла test.log:

Код выше такой же, как это было в предыдущем разделе, за исключением того, что теперь мы добавили имя файла журнала для печати. После того, как мы запустим код с командой python pizza.py, мы должны увидеть новый файл в нашем каталоге, который называется test.log.

Давайте откроем файл test.log с nano (или текстовым редактором по вашему выбору):

Когда файл откроется, мы увидим следующее:

Это похоже на вывод консоли, с которым мы столкнулись в предыдущем разделе, только теперь он находится в файле test.log.

Давайте закроем файл с CTRL+ x и перейдем обратно в файл pizza.py, так что мы можем изменить код.

Мы изменим параметры в двух случаях пиццы, pizza_01 и pizza_02:

С учетом этих изменений, давайте запустим программу снова с командой python pizza.py.

После того как программа запустилась, мы можем открыть наш файл test.log снова с nano\:

Когда мы посмотрим в файл, мы увидим, что были добавлены несколько новых линии, и что предыдущие строки из последнего времени, были сохранены:

Хотя эта информация, безусловно, полезна, мы можем сделать журнал более информативным, добавив дополнительные атрибуты LogRecord. В первую очередь, мы хотели бы добавить удобочитаемый штамп времени, который говорит нам, когда LogRecord была создана.

В зависимости от ваших потребностей, вы можете использовать дополнительные атрибуты LogRecord в коде.

Таблица уровней в Logging

Как разработчик, вы можете приписывать уровень важности в случае, захватывается в регистраторе, добавив уровень серьезности. Уровни серьезности приведены в таблице ниже.

Уровни регистрации технически целые числа (константы), и все они с шагом 10, начиная с NOTSET которое инициализирует регистратор в числовом значении 0.

Вы также можете определить свои собственные уровни относительно предопределенных уровней. Если определить уровень с тем же числовым значением, вы можете перезаписать имя, связанное с этим значением.

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

уровень Числовое значение функция Использовал к
CRITICAL 50 logging.critical() Показать серьезную ошибку, программа может быть не в состоянии продолжать работать
ERROR 40 logging.error() Показать более серьезные проблемы
WARNING 30 logging.warning() Укажите, что-то случилось неожиданное, или может произойти
INFO 20 logging.info() Убедитесь, что все работает, как ожидалось
DEBUG 10 logging.debug() Диагностика проблем, показать подробную информацию

Модуль logging устанавливает уровень по умолчанию на WARNING, так WARNING, ERROR и CRITICAL все будут регистрироваться по умолчанию. В приведенном выше примере, мы изменили конфигурацию, чтобы включать в себя уровень DEBUG со следующим кодом:

Вы можете прочитать больше о командах и работе с отладчиком из официальной документации по модулю logging.

Вывод

Отладка является важным шагом любого проекта разработки программного обеспечения. Модуль logging является частью стандартной библиотеки Python, обеспечивает отслеживание событий, которые происходят в то время как программа работает, и может выводить эти события в отдельный файл журнала, чтобы позволить Вам отслеживать то, что происходит в то время как ваш код работает. Это дает вам возможность отладки кода, основанного на понимании различных событий, которые происходят с запуском программы с течением времени.

Если вы нашли ошибку, пожалуйста, выделите фрагмент текста и нажмите Ctrl+Enter.

Читайте также: