Запись ошибки в файл python

Обновлено: 07.07.2024

В этом руководстве мы изучим основы стандартного модуля логирования в Python.

Что такое логирование в Python?

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

Как работает регистрация

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

Уровень Числовые значения
NOTSET 0
DEBUG 10
INFO 20
WARNING 30
ERROR 40
CRITICAL 50

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

Давайте посмотрим на несколько объектов регистратора, предлагаемых самим модулем.

Давайте разберемся в следующем примере.

Базовые конфигурации

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

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

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

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

Атрибуты%(asctime) добавляют время создания записи журнала. Мы также можем настроить формат с помощью атрибутов datefmt, которые предоставляют ту же функцию, что и модуль datetime.

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

Мы можем использовать f для форматирования данной строки. Он обеспечивает короткий и простой способ обращения со строкой.

Отслеживание трассировки стека

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

Если мы не установим значение exc_info в true, выходные данные не будут сообщать нам об исключении. Было бы сложно отладить ошибку в тысячах строк кода, если она отображает только следующий вывод.

Мы можем использовать любую из опций в методах error(), debug() или critical(), чтобы получить информацию об исключении.

Классы и функции

До сих пор мы видели регистратор по умолчанию с именем root. Модуль ведения журнала используется всякий раз, когда вызываются его функции, такие как logging.debug(), logging.error() и т. д. Мы также можем определить собственное средство ведения журнала, создав объект класса Logger. Здесь мы определяем обычно используемые классы и функции.

Ниже приведены классы и функции, определенные в модуле logging.

Обычно мы работаем с объектами класса Logger, которые создаются с помощью функции logging.getLogger(name). Если метод getLogger() вызывается несколько раз с одним и тем же именем, он вернет ссылку на один и тот же объект регистратора.

Давайте разберемся в следующем примере:

Мы создали собственное имя регистратора first_logger, но, в отличие от корневого регистратора, first_logger не является частью выходного формата. Чтобы отобразить его, передайте его в функцию конфигурации. Тогда результат будет выглядеть следующим образом.

Работа с обработчиками

Давайте разберемся в следующем примере создания обработчиков.

В следующей программе мы создали настраиваемый регистратор с именем logger_obj и создали LogRecord, который хранит всю запись событий регистрации и передает ее всем имеющимся обработчикам: w_handlers и e_handlers.

Заключение

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

Простейший (обратите внимание: не лучший) способ сделать это — использовать много операторов вывода ( print ) своем коде. Это плохая идея, так как вы получите много вывода в консоли, и, скорее всего, удалите инструкции для вывода, как только исправите ошибку.

Как отслеживать ошибки правильно? Логгирование!

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

Вы можете запустить эту программу, выполнив:

Настройка модуля логгирования

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

Измените конструктор класса FirstClass , чтобы настроить модуль logging:

Не забудьте добавить import logging

Конструктор настраивает использование модуля logging и заканчивается записью о том, что настройка завершена.

Если вы снова запустите программу, вы все равно увидите тот же вывод консоли.

Уровень серьёзности логгирования

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

Интересно, это то, чего вы ждали? Должно было быть еще две записи «Число еще увеличивается!!», но они не отобразились. Почему? Ну, дело в том, что у модуля logging 5 уровней серьезности:

  • DEBUG (низший)
  • INFO
  • WARNING
  • ERROR
  • CRITICAL (высший)

Второй пример немного сложнее, так как он обновляет структуру программы для включения пакета с одним модулем:

Файл first_class.py содержит класс FirstClass , который был создан в первом примере:

Чтобы использовать этот модуль, обновите файл app.py в каталоге верхнего уровня и импортируйте класс FirstClass для его использования:

Запустим app.py и проверим файл лога:

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

Вот базовая версия файла second_class.py :

Можно дублировать настройки logger в конструктор этого класса (копия из first_class.py). Это приведет к большому количеству ненужного повторяющегося кода.
Лучше всего переместить настройку logger в файл __init__.py :

Так же нужно упростить def __init__() в first_class.py :

Наконец, обновления в __init__.py приводят к необходимости следующих обновлений в app.py :

Попробуйте снова запустить программу и посмотрите файл лога:

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

Четвертый (и последний) пример расширяет возможности логгирования, добавив входной файл (JSON) для настройки логгера.
Первое изменение для этого примера в __init__.py , чтобы изменить настройки логгера для использования входного файла JSON:

Теперь, когда у нас есть код для обработки входного файла, давайте определим входной файл (python_logging_configuration.json). Обязательно добавьте этот файл в папку верхнего уровня, чтобы он мог быть легко идентифицирован интерпретатором python. Вы должны запустить эту программу из папки верхнего уровня, чтобы сделать входной файл JSON доступным интерпретатору python, поскольку он находится в текущем / рабочем каталоге.
Вот файл конфигурации:

Запустите программу еще раз и посмотрите файл лога:

Если Вам не нравится работать с JSON , входной файл также может быть определен как файл YAML . Вот пример: Good Logging Practice in Python

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

Зачем нужны логи?

logging и Python

Точкой входа в работу с логированием в Python является библиотека logging. На первый взгляд может показаться, что библиотека сложная и запутанная, но потратив некоторое время на её изучение, можно убедиться в обратном. Для меня logging это классический пример дизайна ООП, где композиция преобладает над наследованием, поэтому в исходном коде библиотеки можно встретить множество функциональных классов. Цель этого туториала разобрать по косточкам каждый класс и воссоединить их в единый механизм логирования в Python. Начнём-с.

Logger

Чтобы начать работу с logging необходимо в импортировать библиотеку logging и вызвать функцию getLogger, передав ей имя будущего логера. Функция вернёт инстанс объекта Logger. Логер это рычаг за который мы дёргаем каждый раз, когда нам нужно записать информацию в лог.

Заметьте, что функция getLogger принимает на вход параметр — имя логера. Можно назначать любое имя или __name__ . Вызов getLogger с одинаковым названием вернёт один и тот же инстанс логера.

Я рекомендую использовать в качестве аргумента __name__ , в этом случае не нужно беспокоиться, что разные модули могут ссылаться на один и тот же логер.

Также есть ещё один метод — exception . Его желательно вызывать в блоке except при обработке исключения. В это случае он сможет уловить контекст исключения и записать его в лог:

Handler

Это далеко не полный список. Чтобы посмотреть все, перейдите по ссылке выше. Для указания Handler, необходимо у инстанса Logger вызвать метод addHandler и передать туда инстанс класса Handler. У одного Logger инстанса может быть множество обработчиков.

Пример записи лога в stdout:

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

Formatter

Обратите внимание на строку, которую я передал при инициализации инстанса Formatter :

Filter

Наглядно и понятно, разве logging может быть сложным? 😎

LoggerAdapter

Адаптер нужен для передачи дополнительной контекстной информации при каждой записи лога через Logger. Например, вы написали веб-приложение и вам необходимо в логи дополнительно передавать username пользователя:

extra и не только

Строки в логах это хорошо, а что если я хочу помимо строки дополнительно передавать ответ от веб-сервера? Для этого можно использовать аргумент extra при вызове методов debug , info и т.д. Давайте напишем пример вывода

Теперь вывод значения ключа response можно указать через Formatter (при условии, что response передаётся всегда):

Аргумент extra удобен при написании своих кастомных обработчиков логов (например, отсылка логов в телеграм). Далее я покажу пример кастомного Handler класса для отправки логов в Telegram через бота.

Конфигурация logging

Официальная документация рекомендует конфигурировать logging через python-словарь. Для этого необходимо вызвать функцию logging.config.dictConfig и передать ей специальный словарь. Схема словаря описана здесь. Я лишь вкратце пробегусь по основным ключам:

  • version — ключ указывает версию конфига, рекомендуется наличие этого ключа со значением 1, нужно для обратной совместимости в случае, если в будущем появятся новые версии конфигов.
  • disable_existing_loggers — запрещает или разрешает настройки для существующих логеров (на момент запуска), по умолчанию равен True
  • formatters — настройки форматов логов
  • handlers — настройки для обработчиков логов
  • loggers — настройки существующих логеров

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

Неправда ли удобно? В реальных приложениях настройки выносят в отдельный модуль, который обязательно импортируется на старте, например, модуль в settings.py как в Django. Именно в нём задаются глобальные настройки для всех логеров приложения.

Наследование в logging

Ещё одним удобным механизмом в logging является "наследование" настроек корневого логера его потомками. Наследование задаётся через символ . в названии логера. То есть логер с названием my_package.logger1 унаследует все настройки, заданные для my_package . Давайте обновим пример выше, добавив в LOGGING_CONFIG настройку для my_package

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

Отправляем логи в Telegram

Чтобы создать свой обработчик, необходимо наследоваться от класса Handler и перезаписать метод emit :

При инициализации инстанса класса TelegramBotHandler ему необходимо будет передать токен бота и chat_id . Замечу, что эти настройки можно задать через конфигурирование:

Чтобы обработчик начал свою работу, достаточно в настройках вашего логера прописать новый обработчик:

Заключение

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

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


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

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

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

Как реализовать логирование в 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 обработки исключений.

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

Заключение

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

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

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