Python logging windows event log

Содержание
  1. Простое использование модуля logging в Python.
  2. Содержание:
  3. Логирование событий в файл.
  4. Логирование событий из нескольких модулей.
  5. Регистрация значений переменных.
  6. Форматирование вида отображаемых сообщений.
  7. Отображение даты и времени в сообщениях.
  8. Основные концепции журналирования в Python
  9. Основные концепции журналирования на Python
  10. Уровни журналирования
  11. Форматирование журналов
  12. Обработчик журналирования
  13. Диспетчер журналирования
  14. Практические советы
  15. Логирование в Python
  16. Создаем простой логгер
  17. 8 продвинутых возможностей модуля logging в Python, которые вы не должны пропустить
  18. Основы модуля logging
  19. Логгер
  20. Форматировщик и обработчик
  21. 1. Создавайте заданные пользователем атрибуты объектов класса LogRecord, используя класс LoggerAdapter
  22. Новый атрибут с фиксированным значением
  23. Новый атрибут с динамическим значением
  24. 2. Создавайте определенные пользователем атрибуты объектов класса LogRecord, используя класс Filter
  25. 3. Многопоточность с модулем logging
  26. 4. Многопроцессная обработка с модулем logging — QueueHandler
  27. QueueHandler + «процесс-потребитель»
  28. QueueHandler + QueueListener
  29. SocketHandler
  30. 5. По умолчанию не генерируйте какие-либо журнальные записи библиотеки — NullHandler
  31. Почему нам нужно отделять записи библиотеки от записей приложения?
  32. 6. Делайте ротацию своих файлов журнала — RotatingFileHandler, TimedRotatingFileHandler
  33. 7. Исключения в процессе журналирования
  34. 8. Три разных способа конфигурирования своего логгера
  35. используйте код
  36. use dictConfig
  37. используйте fileConfig

Простое использование модуля logging в Python.

Содержание:

Обратите внимание, что параметры logging.basicConfig() должны передаваться до первого вызова функций ведения журнала. Если в консоли интерпретатора уже была вызвана хотя бы одна функция, то необходимо перезагрузить пакет logging или выйти и снова зайти в консоль интерпретатора. С версии Python-3.8 для этой цели можно использовать ключевой аргумент force=True

Логирование событий в файл.

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

Теперь, если открыть файл example.log то можно увидеть сообщения журнала:

В примере также показано, как можно установить уровень ведения журнала level , который служит порогом для отслеживания событий. Так как установлен уровень отображения событий level=logging.DEBUG , то в файл были записаны все сообщения.

Если запустить скрипт примера несколько раз, то сообщения от последовательных запусков добавляются в файл example.log . Если необходимо, чтобы каждый запуск удалял сообщения от предыдущих запусков, то можно указать аргумент filemode :

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

Если необходимо установить уровень ведения журнала из командной строки, а значение параметра для —log= было получено например в переменную loglevel , то можно использовать функцию getattr для получения значения, которое передается logging.basicConfig() в качестве аргумента level :

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

Логирование событий из нескольких модулей.

Если программа состоит из нескольких модулей, вот пример того, как можно организовать код:

Регистрация сообщений в модуле mylib.py .

Если запустить файл app.py , то в myapp.log увидим следующее:

Код примера можно расширить на несколько модулей, но используя этот простой шаблон, из записей файла журнала будет не понятно из каких модулей поступали сообщения. Если необходимо отслеживать местоположение сообщений, то смотрите раздел «Продвинутое использование модуля logging «.

Регистрация значений переменных.

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

Добавление значений переменных в описание события использует форматирование строк в стиле printf . Такое поведение в модуле logging сохранено для обратной совместимости. С версии Python-3.2 класс logging.Formatter() получил ключевой аргумент style=’%’ , при помощи которого можно установить спецификации форматирования ‘ <' или '$' . Эти спецификации поддерживают подходы, определенные в str.format() и string.Template() соответственно.

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

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

Форматирование вида отображаемых сообщений.

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

Этот запишет в файл example.log следующее:

Обратите внимание, что запись root , имевшая место в предыдущих примерах, исчезла. Для полного набора параметров, которые могут появляться в строке сообщения журналируемого события смотрите документацию по атрибутам объекта logging.LogRecord()

Для простого использования модуля достаточно использования таких атрибутов как levelname (серьезность), message (описание события, включая данные переменных) и возможно понадобится отображение времени, когда произошло событие.

Отображение даты и времени в сообщениях.

Чтобы отобразить дату и время события, необходимо поместить атрибут %(asctime)s в строку формата:

Основные концепции журналирования в Python

По мере того, как усложняется структура приложения, ведение логов (журналирование) становится всё полезнее для разработчика. Логи не только пригодятся в процессе отладки, но и помогут обнаружить скрытые проблемы проекта, а также отследить его производительность.

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

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

22–25 апреля, Онлайн, Беcплатно

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

Основные концепции журналирования на Python

В данном разделе рассмотрим уровни журналирования, форматирование журналов, обработчики и диспетчер журналирования.

Уровни журналирования

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

В Python существует шесть уровней лога. Каждому уровню присвоено целое число, указывающее на важность лога: NOTSET=0, DEBUG=10 , INFO=20 , WARN=30 , ERROR=40 и CRITICAL=50 .

Все уровни последовательно упорядочены ( DEBUG INFO WARN ), кроме NOTSET , который мы отдельно разберём далее.

Форматирование журналов

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

Например, при форматировании записи «hello world»:

в логе она отобразится таким образом:

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

Обработчик журналов — это компонент, который записывает и отображает логи. Например, StreamHandler выводит записи на консоль, FileHandler — в файл, SMTPHandler отправляет на электронную почту.

В каждом обработчике есть два важных поля:

  1. Форматер, добавляющий в лог контекстную информацию.
  2. Уровень лога, который отфильтровывает журналы низшего уровня. Обработчик уровня INFO не будет работать с журналами уровня DEBUG.
Читайте также:  Как редактировать главное меню windows

Стандартная библиотека содержит обработчики, которых в большинстве случаев должно быть достаточно. Самые распространённые — StreamHandler и FileHandler .

Диспетчер журналирования

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

В диспетчере три основных поля.

  1. Распространение. Определяет, должен ли лог обрабатываться родительским диспетчером (по умолчанию эта функция активна).
  2. Уровень. Как и в обработчике, уровень отфильтровывает менее важные журналы. Отличие заключается в том, что уровень проверяется только в дочерних диспетчерах, при распространении вверх уровень не учитывается.
  3. Список обработчиков, в которые направляется попавшая в диспетчер запись. Он позволяет гибко настраивать обработку записей. К примеру, можно создать обработчик, записывающий всё с уровнем DEBUG , и обработчик для электронной почты, работающий только с уровнем CRITICAL . Взаимоотношения диспетчера с обработчиками строятся по принципу издатель-потребитель. Запись передаётся всем обработчикам, как только пройдёт проверку уровня в диспетчере.

Имя диспетчера уникально, таким образом, если диспетчер с именем «toto» уже создан, последующие вызовы logging.getLogger(«toto») будут возвращать один и тот же объект.

Как можно догадаться, диспетчеры выстраиваются в соответствии с иерархией. На вершине находится корневой диспетчер, получить доступ к которому можно с помощью logging.root() . Он вызывается при использовании методов, подобных logging.debug() . По умолчанию корневому журналу присваивается уровень WARN , поэтому каждый журнал с меньшим уровнем (например logging.info(«info») ) игнорируется. Кроме того, обработчик по умолчанию для корневого диспетчера создаётся автоматически, когда добавляется первая запись в журнал уровня выше WARN . Не рекомендуется обращаться к корневому диспетчеру прямо или косвенно с помощью методов, подобных logging.debug() .

При создании нового диспетчера в качестве родительского будет установлен корневой.

Диспетчер использует «запись с точкой», то есть объект с именем «a.b» будет дочерним по отношению к «a». Однако это правило действительно только при существовании «a», иначе родительским всё равно будет корневой диспетчер.

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

По умолчанию уровень нового диспетчера NOTSET , и, поскольку уровень корневого равен WARN , то и эффективный уровень нового будет WARN . Таким образом, если даже к новому диспетчеру прикреплены обработчики, они не вызываются, если уровень лога не превышает WARN.

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

Практические советы

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

  • Настроить корневой диспетчер, но не использовать его в коде, к примеру, никогда не вызывать функцию logging.info() , которая скрыто обращается к корневому диспетчеру. Если вы хотите отловить сообщения об ошибках из используемых библиотек, настройте этот диспетчер, например, на запись в файл, чтобы облегчить отладку. По умолчанию сообщения выводятся только в stderr , поэтому журнал легко потерять.
  • Для использования журналирования создайте новый диспетчер с помощью logging.getLogger(logger name) . Обратите внимание на валидность имени. Чтобы добавить обработчики, можно использовать метод, возвращающий диспетчер.

После этого можно создать и использовать новый диспетчер:

  • Используйте классы RotatingFileHandler, например так, как применён TimedRotatingFileHandler в приведённом выше примере вместо FileHandler. Благодаря этому смена файла журнала будет происходить автоматически при достижении максимального размера или по расписанию.
  • Для автоматического отслеживания сообщений об ошибках используйте такие инструменты, как Sentry, Airbrake, Raygun и тому подобные. Особенно это полезно при работе над веб-приложениями, где логи могут быть весьма многословными и ошибки легко теряются в общей массе. Ещё одно преимущество этих инструментов в том, что они отображают значения переменных при обнаружении ошибок. Таким образом можно определить, какой URL или пользователь спровоцировал проблему и тому подобное.

Логирование в Python

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

  • Создание простого логгера;
  • Использование нескольких модулей для логирования;
  • Форматирование лога;
  • Настройки лога

К концу данного раздела вы сможете уверенно создавать логи для своих приложений. Приступим!

Создаем простой логгер

Создание лога при помощи модуля logging это очень просто. Для начала, будет проще взглянуть на часть кода и объяснить его:

Как и ожидалось, чтобы получит доступ к модулю logging, для начала нужно импортировать модуль. Простейший способ создания лога – это использовать функцию basicConfig модуля logging и передать ей несколько ключевых аргументов. Функция принимает следующее: filename, filemode, format, datefmt, level и stream. В нашем примере, мы передадим её названию файла и уровню логирования, что мы и настроим в INFO.

Существует пять уровней логирования (в порядке возрастания): DEBUG, INFO, WARNING, ERROR и CRITICAL. По умолчанию, если вы запустите этот код несколько раз, он добавится в лог, если он существует. Если вы хотите, чтобы ваш логгер перезаписывал лог, передайте его filemode=”w”, как было указано в комментарии к коду. Говоря о запуске кода, вы должны получить следующий результат, после запуска:

8 продвинутых возможностей модуля logging в Python, которые вы не должны пропустить

Понимайте свою программу без ущерба для производительности

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

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

Читайте также:  Windows 10 шифрует файлы

Основы модуля logging

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

Логгер

Экземпляр, который мы создаем для генерации записей, называют логгером. Он инстанциируется через logger = logging.getLogger(name). Лучшая практика — это использовать name в качестве имени логгера. name включает в себя имя пакета и имя модуля. Имя будет появляться в журнальном сообщении, что поможет разработчикам быстро находить, где оно было сгенерировано.

Форматировщик и обработчик

У любого логгера есть несколько конфигураций, которые могут быть модифицированы. Более продвинутые конфигурации мы обсудим позже, а наиболее ходовые — это форматировщик (прим. пер.: formatter) и обработчик (прим. пер.: handler).

Форматировщик устанавливает структуру журнального сообщения. Каждое журнальное сообщение — это объект класса LogRecord с несколькими атрибутами (имя модуля — один из них). Когда мы определяем форматировщик, мы можем решить, как должно выглядеть журнальное сообщение вместе с этими атрибутами и, возможно, с атрибутами, созданными нами. Стандартный форматировщик выглядит так:

Кастомизированный форматировщик может выглядеть так:

Обработчик задает целевое местонахождение журнальных сообщений. Журнальное сообщение может быть отправлено в более чем одно место. Собственно говоря, модуль logging предоставляет довольно много стандартных обработчиков. Самые популярные — FileHandler, который отправляет записи в файл, и StreamHandler, который отправляет записи в потоки, такие как sys.stderr или sys.stdout. Экземпляр логгера поддерживает ноль или более обработчиков. Если никакие обработчики не определены, тогда он будет отправлять записи в sys.stderr. Если определен более чем один обработчик, тогда целевое местонахождение журнального сообщения зависит от его уровня и от уровня обработчика.

Например, у меня есть FileHandler с уровнем WARNING (прим. пер.: предупреждение) и StreamHandler с уровнем INFO (прим. пер.: информация). Если я напишу в журнал сообщение об ошибке, то оно будет отправлено как в sys.stdout, так и в файл журнала.

Например:

В этом примере мы создали main.py, package1.py, и app_logger.py. Модуль app_logger.py содержит функцию get_logger, которая возвращает экземпляр логгера. Экземпляр логгера включает в себя кастомный форматировщик и два обработчика: StreamHandler с уровнем INFO и FileHandler с уровнем WARNING. Важно установить базовый уровень в INFO или DEBUG (уровень журналирования по умолчанию — WARNING), в противном случае любые записи журнала по уровню ниже, чем WARNING, будут отфильтрованы. И main.py, и package1.py, используют get_logger, чтобы создавать свои собственные логгеры.


Диаграмма Xiaoxu Gao

Записи с уровнем INFO отправляются как в консольный вывод (sys.stdout), так и в файл журнала, а записи с уровнем WARNING пишутся только в файл журнала. Если вы можете полностью понять, что и почему происходит в этом примере, то мы готовы приступить к более продвинутым возможностям.

1. Создавайте заданные пользователем атрибуты объектов класса LogRecord, используя класс LoggerAdapter

Как я упоминал ранее, у LogRecord есть несколько атрибутов. Разработчики могут выбрать наиболее важные атрибуты и использовать в форматировщике. Помимо того, модуль logging также предоставляет возможность добавить в LogRecord определенные пользователем атрибуты.
Один из способов сделать это — использовать LoggerAdapter. Когда вы создаете адаптер, вы передаете ему экземпляр логгера и свои атрибуты (в словаре). Этот класс предоставляет тот же интерфейс, что и класс Logger, поэтому вы все еще можете вызывать методы наподобие logger.info.

Новый атрибут с фиксированным значением

Если вы хотите иметь что-то вроде атрибута с фиксированным значением в журнальном сообщении, например имя приложения, то вы можете использовать стандартный класс LoggerAdapter и получать значение атрибута при создании логгера (прим. пер.: вероятно, получать откуда-либо, чтобы затем передать конструктору). Не забывайте добавлять этот атрибут в форматировщик. Местоположение атрибута вы можете выбрать по своему усмотрению. В следующем коде я добавляю атрибут app, значение которого определяется, когда я создаю логгер.

Новый атрибут с динамическим значением

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

2. Создавайте определенные пользователем атрибуты объектов класса LogRecord, используя класс Filter

Другой способ добавления определенных пользователем атрибутов — использование кастомного Filter. Фильтры предоставляют дополнительную логику для определения того, какие журнальные сообщения выводить. Это шаг после проверки базового уровня журналирования, но до передачи журнального сообщения обработчикам. В дополнение к определению, должно ли журнальное сообщение двигаться дальше, мы также можем вставить новые атрибуты в методе filter().


Диаграмма из официальной документации Python

В этом примере мы добавляем новый атрибут color (прим. пер.: цвет) в методе filter(), значение которого определяется на основе имени уровня в журнальном сообщении. В этом случае имя атрибута снова должно быть добавлено в форматировщик.

3. Многопоточность с модулем logging

Модуль logging на самом деле реализован потокобезопасным способом, поэтому нам не нужны дополнительные усилия. Код ниже показывает, что MainThread и WorkThread разделяют один и тот же экземпляр логгера без проблемы состояния гонки. Также есть встроенный атрибут threadName для форматировщика.

Под капотом модуль logging использует threading.RLock() практически везде. Отличия между RLock от Lock:

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

Lock может быть освобожден любым потоком, а RLock — только тем потоком, который его удерживает.

Любой обработчик, который наследуется от класса Handler, обладает методом handle(), предназначенным для генерации записей. Ниже представлен блок кода метода Handler.handle(). Как видите, обработчик получит и освободит блокировку до и после генерации записи соответственно. Метод emit() может быть реализован по-разному в разных обработчиках.

4. Многопроцессная обработка с модулем logging — QueueHandler

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

Читайте также:  Кодировка linux имя файла

QueueHandler + «процесс-потребитель»

Один из вариантов — использование QueueHandler. Идея заключается в том, чтобы создать экземпляр класса multiprocessing.Queue и поделить его между любым количеством процессов. В примере ниже у нас есть 2 «процесса-производителя», которые отправляют записи журнала в очередь и «процесс-потребитель», читающий записи из очереди и пишущий их в файл журнала.

У записей журнала в очереди будут, вероятно, разные уровни, так что в функции log_processor мы используем logger.log(record.levelno, record.msg) вместо logger.info() или logger.warning(). В конце (прим. пер.: функции main) мы отправляем сигнал, чтобы позволить процессу log_processor остановиться. Деление экземпляра очереди между множеством процессов или потоков — не новшество, но модуль logging как бы помогает нам справиться с этой ситуацией.

QueueHandler + QueueListener

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

SocketHandler

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

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

5. По умолчанию не генерируйте какие-либо журнальные записи библиотеки — NullHandler

На данный момент мы упомянули несколько обработчиков, реализованных модулем logging.
Другой полезный встроенный обработчик — NullHandler. В реализации NullHandler практически ничего нет. Тем не менее, он помогает разработчикам отделить библиотечные записи журнала от записей приложения.

Ниже приведена реализация обработчика NullHandler.

Почему нам нужно отделять записи библиотеки от записей приложения?

Сторонняя библиотека, которая использует logging, по умолчанию не должна выбрасывать вывод журналирования, так как он может быть не нужен разработчику/пользователю приложения, которое использует библиотеку.

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

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

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

Если библиотека не использует NullHandler, но вы хотите отключить записи из библиотеки, то можете установить logging.getLogger(«package»).propagate = False. Если propagate установлен в False, то записи журнала не будут передаваться обработчикам.

6. Делайте ротацию своих файлов журнала — RotatingFileHandler, TimedRotatingFileHandler

RotatingFileHandler поддерживает ротацию файлов журнала, основанную на максимальном размере файла. Здесь должны быть определено два параметра: maxBytes и backupCount. Параметр maxBytes сообщает обработчику, когда делать ротацию журнала. Параметр backupCount — количество файлов журнала. У каждого «продолженного» файла журнала есть суффикс «.1», «.2» в конце имени файла. Если текущее журнальное сообщение вот-вот позволит файлу журнала превысить максимальный размер, то обработчик закроет текущий файл и откроет следующий.

Вот этот пример очень похож на пример из учебника. Должно получиться 6 файлов журнала.

Другой обработчик для ротации файлов — TimeRotatingFileHandler, который позволяет разработчикам создавать ротационные журналы, основываясь на истекшем времени. Условия времени включают: секунду, минуту, час, день, день недели (0=Понедельник) и полночь (журнал продлевается в полночь).

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

7. Исключения в процессе журналирования

Зачастую мы используем logger.error() или logger.exception() при обработке исключений. Но что если сам логгер генерирует исключение? Что случится с программой? Ну, зависит от обстоятельств.

Ошибка логгера обрабатывается, когда обработчик вызывает метод emit(). Это означает, что любое исключение, связанное с форматированием или записью, перехватывается обработчиком, а не поднимается. Если конкретнее, метод handleError() будет выводить трассировку в stderr, и программа продолжится. Если у вас есть кастомный обработчик, наследуемый от класса Handler, то вы можете реализовать свой собственный handleError().

В этом примере во втором журнальном сообщении слишком много аргументов. Поэтому в консольном выводе мы получили трассировку, и выполнение программы все еще могло быть продолжено.

Однако, если исключение произошло за пределами emit(), то оно может быть поднято, и программа остановится. Например, в коде ниже мы добавляем дополнительный атрибут id в logger.info без его обработки в LoggerAdapter. Эта ошибка не обработана и приводит к остановке программы.

8. Три разных способа конфигурирования своего логгера

Последний пункт, которым я хотел поделиться, — о конфигурировании своего логгера. Есть три способа конфигурирования логгера.

используйте код

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

use dictConfig

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

используйте fileConfig

И последний, но не менее важный, третий вариант — использовать logging.config.fileConfig Конфигурация записывается в отдельный файл формата .ini.

Есть возможность обновлять конфигурацию во время выполнения программы через сервер конфигурации. Учебник показывает пример как со стороны клиента, так и со стороны сервера. Конфигурация обновляется посредством подключения через сокет, а на стороне клиента мы используем c = logging.config.listen(PORT) c.start(), чтобы получать самую новую конфигурацию.

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

Оцените статью