Документация Python неофициальный перевод

logging.md

616 строк · 83.8 КБ · обычная страница · сырой текст · скачать

1> **Источник:** https://python-all.ru/3.12/howto/logging.html2>3> «Документация Python на русском» – неофициальный перевод официальной документации Python: версии от 2.6 до 3.16, полнотекстовый поиск, английский оригинал рядом с переводом. Эта Markdown-версия страницы предназначена для работы с LLM: вставьте её в ChatGPT, Claude или Cursor.45---67# Руководство по логированию89**Автор:**1011Vinay Sajip \<vinay\_sajip at red-dove dot com\>1213На этой странице содержится учебная информация. Ссылки на справочную информацию и поваренную книгу по логированию можно найти в [Другие ресурсы](https://python-all.ru/3.12/howto/logging.html#tutorial-ref-links).1415## Базовое руководство по логированию1617Логирование – это средство отслеживания событий, происходящих при выполнении программного обеспечения. Разработчик добавляет в код вызовы логирования, чтобы указать, что произошли определенные события. Событие описывается информационным сообщением, которое может содержать переменные данные (т. е. данные, которые потенциально различаются для каждого экземпляра события). События также имеют важность, которую разработчик присваивает событию; эту важность также называют *уровнем* или *серьезностью*.1819### Когда использовать логирование2021Вы можете получить доступ к функциям логирования, создав логгер через `logger = getLogger(__name__)`, а затем вызывая методы логгера [`debug()`](https://python-all.ru/3.12/library/logging.html#logging.Logger.debug), [`info()`](https://python-all.ru/3.12/library/logging.html#logging.Logger.info), [`warning()`](https://python-all.ru/3.12/library/logging.html#logging.Logger.warning), [`error()`](https://python-all.ru/3.12/library/logging.html#logging.Logger.error) и [`critical()`](https://python-all.ru/3.12/library/logging.html#logging.Logger.critical). Чтобы определить, когда использовать логирование, и увидеть, какие методы логгера применять в том или ином случае, обратитесь к таблице ниже. Для каждой из распространенных задач в ней указан наилучший инструмент.2223| Задача, которую нужно выполнить | Лучший инструмент для решения задачи |24| --- | --- |25| Вывод на консоль при обычном использовании скрипта или программы командной строки | [`print()`](https://python-all.ru/3.12/library/functions.html#print) |26| Сообщать о событиях, происходящих при нормальной работе программы (например, для мониторинга состояния или анализа неисправностей) | Метод [`info()`](https://python-all.ru/3.12/library/logging.html#logging.Logger.info) логгера (или метод [`debug()`](https://python-all.ru/3.12/library/logging.html#logging.Logger.debug) для очень подробного вывода в диагностических целях) |27| Выдавать предупреждение о конкретном событии времени выполнения | [`warnings.warn()`](https://python-all.ru/3.12/library/warnings.html#warnings.warn) в библиотечном коде, если проблема устранима и клиентское приложение следует изменить для устранения предупреждения Метод [`warning()`](https://python-all.ru/3.12/library/logging.html#logging.Logger.warning) логгера, если клиентское приложение не может повлиять на ситуацию, но событие всё же следует отметить |28| Сообщить об ошибке, связанной с конкретным событием времени выполнения | Вызвать исключение |29| Сообщить о подавлении ошибки без вызова исключения (например, обработчик ошибок в долго работающем серверном процессе) | Метод [`error()`](https://python-all.ru/3.12/library/logging.html#logging.Logger.error), [`exception()`](https://python-all.ru/3.12/library/logging.html#logging.Logger.exception) или [`critical()`](https://python-all.ru/3.12/library/logging.html#logging.Logger.critical) логгера, в зависимости от конкретной ошибки и области применения |3031Методы логгера названы в соответствии с уровнем или серьезностью событий, которые они отслеживают. Стандартные уровни и их применимость описаны ниже (в порядке возрастания серьезности):3233| Уровень | Когда применяется |34| --- | --- |35| `DEBUG` | Подробная информация, обычно представляющая интерес только при диагностике проблем. |36| `INFO` | Подтверждение того, что всё работает как ожидается. |37| `WARNING` | Указание на то, что произошло что-то неожиданное, или признак возможной проблемы в ближайшем будущем (например, «мало места на диске»). Программа по-прежнему работает как ожидается. |38| `ERROR` | Из-за более серьезной проблемы программа не смогла выполнить некоторую функцию. |39| `CRITICAL` | Серьезная ошибка, указывающая на то, что программа, возможно, не сможет продолжить работу. |4041Уровень по умолчанию – `WARNING`, что означает, что будут отслеживаться только события этого уровня и выше, если пакет logging не настроен иначе.4243Отслеживаемые события можно обрабатывать разными способами. Самый простой способ – выводить их на консоль. Другой распространенный способ – записывать их в файл на диске.4445### Простой пример4647Вот очень простой пример:4849```python50import logging51logging.warning('Watch out!')  # выведет сообщение в консоль52logging.info('I told you so')  # ничего не выведет53```5455Если ввести эти строки в скрипт и запустить его, на консоли появится:5657```text58WARNING:root:Watch out!59```6061выводится на консоль. Сообщение `INFO` не появляется, потому что уровень по умолчанию – `WARNING`. Выводимое сообщение включает указание уровня и описание события, переданное в вызове логирования, то есть «Watch out!». Фактический вывод можно форматировать довольно гибко, если это необходимо; параметры форматирования будут объяснены позже.6263Обратите внимание: в этом примере функции используются непосредственно из модуля `logging`, например `logging.debug`, вместо создания регистратора и вызова его методов. Эти функции работают с корневым регистратором, но могут быть удобны, так как они вызывают [`basicConfig()`](https://python-all.ru/3.12/library/logging.html#logging.basicConfig) автоматически, если он ещё не был вызван, как в этом примере. Однако в больших программах обычно требуется явно управлять конфигурацией логирования, поэтому по этой и другим причинам лучше создавать регистраторы и вызывать их методы.6465### Запись логов в файл6667Очень распространенная ситуация – запись событий логирования в файл, так что давайте рассмотрим это далее. Обязательно попробуйте следующий код в только что запущенном интерпретаторе Python, а не продолжайте сессию, описанную выше:6869```python70import logging71logger = logging.getLogger(__name__)72logging.basicConfig(filename='example.log', encoding='utf-8', level=logging.DEBUG)73logger.debug('This message should go to the log file')74logger.info('So should this')75logger.warning('And this, too')76logger.error('And non-ASCII stuff, too, like Øresund and Malmö')77```7879Изменено в версии 3.9: Был добавлен аргумент *encoding*. В более ранних версиях Python или если аргумент не указан, используется кодировка по умолчанию, применяемая [`open()`](https://python-all.ru/3.12/library/functions.html#open). Хотя это не показано в примере выше, теперь также можно передать аргумент *errors*, который определяет, как обрабатывать ошибки кодирования. Допустимые значения и значение по умолчанию см. в документации [`open()`](https://python-all.ru/3.12/library/functions.html#open).8081Теперь, если мы откроем файл и посмотрим, что получилось, мы должны увидеть сообщения журнала:8283```text84DEBUG:__main__:This message should go to the log file85INFO:__main__:So should this86WARNING:__main__:And this, too87ERROR:__main__:And non-ASCII stuff, too, like Øresund and Malmö88```8990Этот пример также показывает, как можно задать уровень логирования, который служит порогом для отслеживания. В данном случае, поскольку мы установили порог `DEBUG`, были выведены все сообщения.9192Чтобы задать уровень логирования из параметра командной строки, например:9394```text95--log=INFO96```9798а значение параметра, переданного для `--log`, хранится в переменной *loglevel*, можно использовать:99100```python101getattr(logging, loglevel.upper())102```103104чтобы получить значение, которое затем передаётся в [`basicConfig()`](https://python-all.ru/3.12/library/logging.html#logging.basicConfig) через аргумент *level*. Стоит проверить любые вводимые пользователем значения на ошибки, как в следующем примере:105106```python107# предполагая, что loglevel привязан к строковому значению, полученному из108# аргумента командной строки. Преобразование в верхний регистр позволяет пользователю109# указать --log=DEBUG или --log=debug110numeric_level = getattr(logging, loglevel.upper(), None)111if not isinstance(numeric_level, int):112    raise ValueError('Invalid log level: %s' % loglevel)113logging.basicConfig(level=numeric_level, ...)114```115116Вызов [`basicConfig()`](https://python-all.ru/3.12/library/logging.html#logging.basicConfig) должен выполняться *до* любых вызовов методов логгера, таких как [`debug()`](https://python-all.ru/3.12/library/logging.html#logging.Logger.debug), [`info()`](https://python-all.ru/3.12/library/logging.html#logging.Logger.info) и т.д. В противном случае это событие логирования может быть обработано не так, как нужно.117118Если запускать этот скрипт несколько раз, сообщения от последовательных запусков добавляются в файл *example.log*. Чтобы каждый запуск начинался с чистого листа, без сохранения сообщений от предыдущих запусков, можно указать аргумент *filemode*, изменив вызов в предыдущем примере на:119120```python121logging.basicConfig(filename='example.log', filemode='w', level=logging.DEBUG)122```123124Вывод будет таким же, как и раньше, но лог-файл больше не дополняется, поэтому сообщения от предыдущих запусков теряются.125126### Логирование переменных данных127128Для логирования переменных данных используется строка формата сообщения описания события, а сами переменные данные передаются в качестве аргументов. Например:129130```python131import logging132logging.warning('%s before you %s', 'Look', 'leap!')133```134135выведет:136137```text138WARNING:root:Look before you leap!139```140141Как видно, встраивание переменных данных в сообщение описания события использует старый, %-стиль форматирования строк. Это сделано для обратной совместимости: пакет logging появился раньше, чем новые способы форматирования, такие как [`str.format()`](https://python-all.ru/3.12/library/stdtypes.html#str.format) и [`string.Template`](https://python-all.ru/3.12/library/string.html#string.Template). Эти новые способы форматирования *поддерживаются*, но их рассмотрение выходит за рамки этого руководства: см. [Использование определённых стилей форматирования во всём приложении](https://python-all.ru/3.12/howto/logging-cookbook.html#formatting-styles) для получения дополнительной информации.142143### Изменение формата отображаемых сообщений144145Для изменения формата отображения сообщений нужно указать нужный формат:146147```python148import logging149logging.basicConfig(format='%(levelname)s:%(message)s', level=logging.DEBUG)150logging.debug('This message should appear on the console')151logging.info('So should this')152logging.warning('And this, too')153```154155что выведет:156157```text158DEBUG:This message should appear on the console159INFO:So should this160WARNING:And this, too161```162163Обратите внимание, что ‘root’, который появлялся в предыдущих примерах, исчез. Полный список того, что может присутствовать в строках формата, можно найти в документации [атрибутов LogRecord](https://python-all.ru/3.12/library/logging.html#logrecord-attributes), но для простого использования достаточно *levelname* (уровень серьёзности), *message* (описание события, включая переменные данные) и, возможно, отображения времени события. Это описано в следующем разделе.164165### Отображение даты/времени в сообщениях166167Для отображения даты и времени события в строку формата помещается ‘%(asctime)s’:168169```python170import logging171logging.basicConfig(format='%(asctime)s %(message)s')172logging.warning('is when this event was logged.')173```174175что выведет примерно следующее:176177```text1782010-12-12 11:41:42,612 is when this event was logged.179```180181Формат отображения даты/времени по умолчанию (показан выше) похож на ISO8601 или [**RFC 3339**](https://python-all.ru/3.12/howto/logging.html). Если требуется более точный контроль форматирования даты/времени, укажите аргумент *datefmt* для `basicConfig`, как в этом примере:182183```python184import logging185logging.basicConfig(format='%(asctime)s %(message)s', datefmt='%m/%d/%Y %I:%M:%S %p')186logging.warning('is when this event was logged.')187```188189что отобразит примерно следующее:190191```text19212/12/2010 11:46:36 AM is when this event was logged.193```194195Формат аргумента *datefmt* такой же, как поддерживается [`time.strftime()`](https://python-all.ru/3.12/library/time.html#time.strftime).196197### Следующие шаги198199На этом базовое руководство заканчивается. Этого должно быть достаточно, чтобы начать работу с логированием. Пакет logging предлагает гораздо больше возможностей, но чтобы извлечь из него максимум, потребуется уделить немного времени чтению следующих разделов. Если готовы, возьмите любимый напиток и продолжайте.200201Если требования к логированию просты, используйте приведённые выше примеры, чтобы добавить логирование в свои скрипты. При возникновении проблем или непонимания задайте вопрос в группе Usenet comp.lang.python (доступной по адресу [https://groups.google.com/g/comp.lang.python](https://python-all.ru/3.12/howto/logging.html)), и вы довольно скоро получите помощь.202203Всё ещё здесь? Можно продолжить чтение следующих разделов, которые представляют собой немного более продвинутое/углублённое руководство по сравнению с базовым. После этого можно заглянуть в [Logging Cookbook](https://python-all.ru/3.12/howto/logging-cookbook.html#logging-cookbook).204205## Расширенное руководство по логированию206207Библиотека logging использует модульный подход и предлагает несколько категорий компонентов: регистраторы, обработчики, фильтры и форматировщики.208209- Регистраторы предоставляют интерфейс, который напрямую используется кодом приложения.210- Обработчики отправляют записи журнала (созданные регистраторами) в соответствующее назначение.211- Фильтры предоставляют более детальный механизм для определения того, какие записи журнала выводить.212- Форматировщики определяют расположение записей журнала в конечном выводе.213214Информация о событии логирования передаётся между регистраторами, обработчиками, фильтрами и форматировщиками в экземпляре [`LogRecord`](https://python-all.ru/3.12/library/logging.html#logging.LogRecord).215216Логирование выполняется вызовом методов экземпляров класса [`Logger`](https://python-all.ru/3.12/library/logging.html#logging.Logger) (далее называемых *регистраторами*). Каждый экземпляр имеет имя, и они концептуально организованы в иерархию пространств имён с использованием точек в качестве разделителей. Например, регистратор с именем ‘scan’ является родителем для регистраторов ‘scan.text’, ‘scan.html’ и ‘scan.pdf’. Имена регистраторов могут быть любыми, они указывают на область приложения, в которой возникло регистрируемое сообщение.217218Хороший подход при именовании логгеров – использовать модульный логгер в каждом модуле, где используется логирование, названный следующим образом:219220```python221logger = logging.getLogger(__name__)222```223224Это означает, что имена логгеров отслеживают иерархию пакетов/модулей, и из одного только имени логгера интуитивно понятно, где регистрируются события.225226Корень иерархии логгеров называется корневым логгером. Это логгер, используемый функциями [`debug()`](https://python-all.ru/3.12/library/logging.html#logging.debug), [`info()`](https://python-all.ru/3.12/library/logging.html#logging.info), [`warning()`](https://python-all.ru/3.12/library/logging.html#logging.warning), [`error()`](https://python-all.ru/3.12/library/logging.html#logging.error) и [`critical()`](https://python-all.ru/3.12/library/logging.html#logging.critical), которые просто вызывают одноимённый метод корневого логгера. Функции и методы имеют одинаковые сигнатуры. Имя корневого логгера выводится как 'root' в записях журнала.227228It is, of course, possible to log messages to different destinations. Support is included in the package for writing log messages to files, HTTP GET/POST locations, email via SMTP, generic sockets, queues, or OS-specific logging mechanisms such as syslog or the Windows NT event log. Destinations are served by *handler* classes. You can create your own log destination class if you have special requirements not met by any of the built-in handler classes.229230По умолчанию ни одно место назначения не задано для сообщений журнала. Вы можете указать место назначения (например, консоль или файл) с помощью [`basicConfig()`](https://python-all.ru/3.12/library/logging.html#logging.basicConfig), как в примерах из руководства. Если вызвать функции [`debug()`](https://python-all.ru/3.12/library/logging.html#logging.debug), [`info()`](https://python-all.ru/3.12/library/logging.html#logging.info), [`warning()`](https://python-all.ru/3.12/library/logging.html#logging.warning), [`error()`](https://python-all.ru/3.12/library/logging.html#logging.error) и [`critical()`](https://python-all.ru/3.12/library/logging.html#logging.critical), они проверят, не задано ли место назначения; и если не задано, то установят местом назначения консоль (`sys.stderr`) и формат по умолчанию для отображаемого сообщения, прежде чем делегировать вывод корневому логгеру для фактической записи сообщения.231232Формат по умолчанию, устанавливаемый [`basicConfig()`](https://python-all.ru/3.12/library/logging.html#logging.basicConfig) для сообщений:233234```text235severity:logger name:message236```237238Вы можете изменить это, передав строку формата в [`basicConfig()`](https://python-all.ru/3.12/library/logging.html#logging.basicConfig) с именованным аргументом *format*. Все параметры построения строки формата описаны в [Объекты Formatter](https://python-all.ru/3.12/library/logging.html#formatter-objects).239240### Поток логирования241242Поток информации о событиях логирования в логгерах и обработчиках показан на следующей диаграмме.243244Invert color in dark mode Поток логгера Создать LogRecord Вызов логирования в пользовательском коде, например logger.info(...) Стоп Отклоняет ли фильтр, прикреплённый к логгеру, отклоняет запись? Передать запись обработчикам текущего логгера Включено ли распространение для текущего логгера? Есть ли родительский логгер? Установить текущий логгер как родительский Хотя бы один обработчик в иерархии? Использовать lastResort обработчик Обработчик включён для уровня записи? Отклоняет ли фильтр, прикреплённый к обработчику запись? Стоп Выдать (включая форматирование) Поток обработчика Логгер включён для уровня вызова? Нет Да Да Нет Нет Да Да Нет Нет Да Нет Да Нет Да Запись передана обработчику245246### Логгеры247248[`Logger`](https://python-all.ru/3.12/library/logging.html#logging.Logger) объекты выполняют тройную задачу. Во-первых, они предоставляют прикладному коду несколько методов, чтобы приложения могли регистрировать сообщения во время выполнения. Во-вторых, объекты логгера определяют, на какие сообщения журнала следует реагировать, на основе важности (механизм фильтрации по умолчанию) или объектов-фильтров. В-третьих, объекты логгера передают соответствующие сообщения журнала всем заинтересованным обработчикам.249250Наиболее часто используемые методы объектов логгера делятся на две категории: настройка и отправка сообщений.251252Ниже приведены наиболее распространённые методы настройки:253254- [`Logger.setLevel()`](https://python-all.ru/3.12/library/logging.html#logging.Logger.setLevel) задаёт наименьший уровень важности сообщения журнала, которое будет обрабатывать логгер, где DEBUG – это наименьший встроенный уровень важности, а CRITICAL – наибольший. Например, если уровень важности – INFO, логгер будет обрабатывать только сообщения уровней INFO, WARNING, ERROR и CRITICAL и будет игнорировать сообщения DEBUG.255- [`Logger.addHandler()`](https://python-all.ru/3.12/library/logging.html#logging.Logger.addHandler) и [`Logger.removeHandler()`](https://python-all.ru/3.12/library/logging.html#logging.Logger.removeHandler) добавляют и удаляют объекты-обработчики из объекта логгера. Обработчики подробно рассматриваются в разделе [Обработчики](https://python-all.ru/3.12/howto/logging.html#handler-basic).256- [`Logger.addFilter()`](https://python-all.ru/3.12/library/logging.html#logging.Logger.addFilter) и [`Logger.removeFilter()`](https://python-all.ru/3.12/library/logging.html#logging.Logger.removeFilter) добавляют и удаляют объекты фильтров из объекта регистратора. Фильтры более подробно рассмотрены в разделе [Объекты фильтров](https://python-all.ru/3.12/library/logging.html#filter).257258Необязательно всегда вызывать эти методы для каждого создаваемого регистратора. См. два последних абзаца в этом разделе.259260После настройки объекта регистратора следующие методы создают сообщения журнала:261262- [`Logger.debug()`](https://python-all.ru/3.12/library/logging.html#logging.Logger.debug), [`Logger.info()`](https://python-all.ru/3.12/library/logging.html#logging.Logger.info), [`Logger.warning()`](https://python-all.ru/3.12/library/logging.html#logging.Logger.warning), [`Logger.error()`](https://python-all.ru/3.12/library/logging.html#logging.Logger.error) и [`Logger.critical()`](https://python-all.ru/3.12/library/logging.html#logging.Logger.critical) – все они создают записи журнала с сообщением и уровнем, соответствующим названию метода. Сообщение на самом деле является строкой форматирования, которая может содержать стандартный синтаксис подстановки строк `%s`, `%d`, `%f` и так далее. Остальные аргументы – это список объектов, соответствующих полям подстановки в сообщении. Что касается `**kwargs`, методы журналирования учитывают только ключевое слово `exc_info` и используют его для определения необходимости записи информации об исключении.263- [`Logger.exception()`](https://python-all.ru/3.12/library/logging.html#logging.Logger.exception) создаёт сообщение журнала, похожее на [`Logger.error()`](https://python-all.ru/3.12/library/logging.html#logging.Logger.error). Отличие в том, что [`Logger.exception()`](https://python-all.ru/3.12/library/logging.html#logging.Logger.exception) дополнительно выводит трассировку стека. Вызывайте этот метод только из обработчика исключений.264- [`Logger.log()`](https://python-all.ru/3.12/library/logging.html#logging.Logger.log) принимает уровень журнала как явный аргумент. Это немного многословнее для записи сообщений, чем использование перечисленных выше удобных методов по уровню, но так можно записывать сообщения с пользовательскими уровнями.265266[`getLogger()`](https://python-all.ru/3.12/library/logging.html#logging.getLogger) возвращает ссылку на экземпляр регистратора с указанным именем, если оно задано, или `root`, если нет. Имена представляют собой иерархические структуры, разделённые точками. При многократных вызовах [`getLogger()`](https://python-all.ru/3.12/library/logging.html#logging.getLogger) с одним и тем же именем возвращается ссылка на тот же объект регистратора. Регистраторы, находящиеся ниже в иерархическом списке, являются дочерними по отношению к регистраторам выше. Например, для регистратора с именем `foo` регистраторы с именами `foo.bar`, `foo.bar.baz` и `foo.bam` являются потомками `foo`.267268У регистраторов есть понятие *эффективного уровня*. Если уровень явно не задан для регистратора, вместо него используется уровень его родителя. Если у родителя нет явно заданного уровня, проверяется *его* родитель, и так далее – перебираются все предки, пока не будет найден явно заданный уровень. Корневой регистратор всегда имеет явно заданный уровень (`WARNING` по умолчанию). При принятии решения об обработке события используется эффективный уровень регистратора, чтобы определить, передаётся ли событие обработчикам регистратора.269270Дочерние регистраторы распространяют сообщения вверх к обработчикам, связанным с их родительскими регистраторами. Из-за этого нет необходимости определять и настраивать обработчики для всех регистраторов, используемых в приложении. Достаточно настроить обработчики для регистратора верхнего уровня и по мере необходимости создавать дочерние регистраторы. (Однако можно отключить распространение, установив для атрибута *propagate* регистратора значение `False`.)271272### Обработчики273274Объекты [`Handler`](https://python-all.ru/3.12/library/logging.html#logging.Handler) отвечают за отправку соответствующих сообщений журнала (на основе их серьёзности) в указанное назначение обработчика. Объекты [`Logger`](https://python-all.ru/3.12/library/logging.html#logging.Logger) могут добавлять к себе ноль или более объектов-обработчиков с помощью метода [`addHandler()`](https://python-all.ru/3.12/library/logging.html#logging.Logger.addHandler). Например, приложение может захотеть отправлять все сообщения журнала в файл, все сообщения уровня ERROR и выше – в stdout, а все критические сообщения – на адрес электронной почты. Для этого потребуется три отдельных обработчика, каждый из которых отвечает за отправку сообщений определённого уровня в конкретное место.275276Стандартная библиотека включает множество типов обработчиков (см. [Полезные обработчики](https://python-all.ru/3.12/howto/logging.html#useful-handlers)); в руководствах в основном используются [`StreamHandler`](https://python-all.ru/3.12/library/logging.handlers.html#logging.StreamHandler) и [`FileHandler`](https://python-all.ru/3.12/library/logging.handlers.html#logging.FileHandler) в примерах.277278В обработчике очень мало методов, которые должны беспокоить разработчиков приложений. Единственные методы обработчика, актуальные для разработчиков, использующих встроенные объекты-обработчики (то есть не создающих собственные обработчики), – это следующие методы настройки:279280- Метод [`setLevel()`](https://python-all.ru/3.12/library/logging.html#logging.Handler.setLevel), как и в объектах регистратора, задаёт наименьший уровень серьёзности, который будет отправлен в соответствующее место назначения. Зачем нужны два метода [`setLevel()`](https://python-all.ru/3.12/library/logging.html#logging.Handler.setLevel)? Уровень, установленный в регистраторе, определяет, сообщения какой серьёзности он будет передавать своим обработчикам. Уровень, установленный в каждом обработчике, определяет, какие сообщения этот обработчик будет отправлять дальше.281- [`setFormatter()`](https://python-all.ru/3.12/library/logging.html#logging.Handler.setFormatter) выбирает объект Formatter для использования этим обработчиком.282- [`addFilter()`](https://python-all.ru/3.12/library/logging.html#logging.Handler.addFilter) и [`removeFilter()`](https://python-all.ru/3.12/library/logging.html#logging.Handler.removeFilter) соответственно настраивают и удаляют объекты фильтров для обработчиков.283284Код приложения не должен напрямую создавать экземпляры [`Handler`](https://python-all.ru/3.12/library/logging.html#logging.Handler) и использовать их. Вместо этого класс [`Handler`](https://python-all.ru/3.12/library/logging.html#logging.Handler) является базовым, который определяет интерфейс для всех обработчиков и устанавливает некоторое поведение по умолчанию, которое могут использовать (или переопределять) дочерние классы.285286### Форматировщики287288Объекты Formatter настраивают окончательный порядок, структуру и содержимое сообщения журнала. В отличие от базового класса [`logging.Handler`](https://python-all.ru/3.12/library/logging.html#logging.Handler), код приложения может создавать экземпляры классов форматировщиков, хотя, возможно, потребуется создать подкласс форматировщика, если приложению требуется особое поведение. Конструктор принимает три необязательных аргумента: строку формата сообщения, строку формата даты и указатель стиля.289290#### `logging.Formatter.__init__(fmt=None, datefmt=None, style='%')`291292Если строка формата сообщения не задана, по умолчанию используется исходное сообщение. Если строка формата даты не задана, формат даты по умолчанию:293294```text295%Y-%m-%d %H:%M:%S296```297298с добавлением миллисекунд в конце. `style` – это одно из значений `'%'`, `'{'` или `'$'`. Если ни одно из них не указано, будет использоваться `'%'`.299300Если `style` равен `'%'`, строка формата сообщения использует подстановку строк в стиле `%(<dictionary key>)s`; возможные ключи описаны в разделе [Атрибуты LogRecord](https://python-all.ru/3.12/library/logging.html#logrecord-attributes). Если стиль `'{'`, предполагается, что строка формата сообщения совместима с [`str.format()`](https://python-all.ru/3.12/library/stdtypes.html#str.format) (с использованием именованных аргументов), а если стиль `'$'`, строка формата сообщения должна соответствовать тому, что ожидается от [`string.Template.substitute()`](https://python-all.ru/3.12/library/string.html#string.Template.substitute).301302Изменено в версии 3.2: Добавлен параметр `style`.303304Следующая строка формата сообщения будет записывать время в удобочитаемом формате, уровень серьёзности сообщения и содержимое сообщения в указанном порядке:305306```python307'%(asctime)s - %(levelname)s - %(message)s'308```309310Форматировщики используют настраиваемую пользователем функцию для преобразования времени создания записи в кортеж. По умолчанию используется [`time.localtime()`](https://python-all.ru/3.12/library/time.html#time.localtime); чтобы изменить это для конкретного экземпляра форматировщика, установите атрибут `converter` экземпляра в функцию с той же сигнатурой, что и [`time.localtime()`](https://python-all.ru/3.12/library/time.html#time.localtime) или [`time.gmtime()`](https://python-all.ru/3.12/library/time.html#time.gmtime). Чтобы изменить это для всех форматировщиков (например, если нужно показывать всё время в GMT), установите атрибут `converter` в классе Formatter (в `time.gmtime` для отображения GMT).311312### Настройка журналирования313314Программисты могут настраивать журналирование тремя способами:3153161. Создание регистраторов, обработчиков и форматировщиков явно с помощью кода Python, вызывающего перечисленные выше методы настройки.3172. Создание файла конфигурации журналирования и его чтение с помощью функции [`fileConfig()`](https://python-all.ru/3.12/library/logging.config.html#logging.config.fileConfig).3183. Создание словаря с информацией о конфигурации и передача его функции [`dictConfig()`](https://python-all.ru/3.12/library/logging.config.html#logging.config.dictConfig).319320Справочную документацию по двум последним вариантам см. в разделе [Функции конфигурации](https://python-all.ru/3.12/library/logging.config.html#logging-config-api). Следующий пример настраивает очень простой регистратор, обработчик для консоли и простой форматировщик с помощью кода Python:321322```python323import logging324325# создать логгер326logger = logging.getLogger('simple_example')327logger.setLevel(logging.DEBUG)328329# создать обработчик консоли и установить уровень DEBUG330ch = logging.StreamHandler()331ch.setLevel(logging.DEBUG)332333# создать форматтер334formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')335336# добавить форматтер в ch337ch.setFormatter(formatter)338339# добавить ch в логгер340logger.addHandler(ch)341342# код 'приложения'343logger.debug('debug message')344logger.info('info message')345logger.warning('warn message')346logger.error('error message')347logger.critical('critical message')348```349350Запуск этого модуля из командной строки выдает следующий результат:351352```console353$ python simple_logging_module.py3542005-03-19 15:10:26,618 - simple_example - DEBUG - debug message3552005-03-19 15:10:26,620 - simple_example - INFO - info message3562005-03-19 15:10:26,695 - simple_example - WARNING - warn message3572005-03-19 15:10:26,697 - simple_example - ERROR - error message3582005-03-19 15:10:26,773 - simple_example - CRITICAL - critical message359```360361Следующий модуль Python создает логгер, обработчик и форматтер, почти идентичные приведенным в примере выше, с единственным отличием – имена объектов.362363```python364import logging365import logging.config366367logging.config.fileConfig('logging.conf')368369# создать логгер370logger = logging.getLogger('simpleExample')371372# код 'приложения'373logger.debug('debug message')374logger.info('info message')375logger.warning('warn message')376logger.error('error message')377logger.critical('critical message')378```379380Вот файл logging.conf:381382```ini383[loggers]384keys=root,simpleExample385386[handlers]387keys=consoleHandler388389[formatters]390keys=simpleFormatter391392[logger_root]393level=DEBUG394handlers=consoleHandler395396[logger_simpleExample]397level=DEBUG398handlers=consoleHandler399qualname=simpleExample400propagate=0401402[handler_consoleHandler]403class=StreamHandler404level=DEBUG405formatter=simpleFormatter406args=(sys.stdout,)407408[formatter_simpleFormatter]409format=%(asctime)s - %(name)s - %(levelname)s - %(message)s410```411412Результат почти идентичен примеру без файла конфигурации:413414```console415$ python simple_logging_config.py4162005-03-19 15:38:55,977 - simpleExample - DEBUG - debug message4172005-03-19 15:38:55,979 - simpleExample - INFO - info message4182005-03-19 15:38:56,054 - simpleExample - WARNING - warn message4192005-03-19 15:38:56,055 - simpleExample - ERROR - error message4202005-03-19 15:38:56,130 - simpleExample - CRITICAL - critical message421```422423Видно, что подход с файлом конфигурации имеет несколько преимуществ перед подходом с кодом Python: в первую очередь разделение конфигурации и кода, а также возможность для не-программистов легко изменять параметры журналирования.424425> **Предупреждение**426>427> Функция [`fileConfig()`](https://python-all.ru/3.12/library/logging.config.html#logging.config.fileConfig) принимает параметр по умолчанию `disable_existing_loggers`, который по умолчанию равен `True` из соображений обратной совместимости. Это может быть как желательно, так и нет, поскольку это приведет к отключению любых не корневых логгеров, существовавших до вызова [`fileConfig()`](https://python-all.ru/3.12/library/logging.config.html#logging.config.fileConfig), если они (или их предок) не указаны явно в конфигурации. Обратитесь к справочной документации за дополнительной информацией и укажите `False` для этого параметра, если хотите.428>429> Словарь, передаваемый в [`dictConfig()`](https://python-all.ru/3.12/library/logging.config.html#logging.config.dictConfig), может также содержать логическое значение с ключом `disable_existing_loggers`; если оно не указано явно в словаре, оно по умолчанию интерпретируется как `True`. Это приводит к описанному выше поведению отключения логгера, которое может быть нежелательным – в таком случае укажите ключ явно со значением `False`.430431Обратите внимание, что имена классов, указанные в файлах конфигурации, должны быть либо относительными к модулю logging, либо абсолютными значениями, которые можно разрешить с помощью обычных механизмов импорта. Таким образом, можно использовать либо [`WatchedFileHandler`](https://python-all.ru/3.12/library/logging.handlers.html#logging.handlers.WatchedFileHandler) (относительно модуля logging), либо `mypackage.mymodule.MyHandler` (для класса, определенного в пакете `mypackage` и модуле `mymodule`, где `mypackage` доступен в пути импорта Python).432433В Python 3.2 был представлен новый способ настройки журналирования с использованием словарей для хранения информации о конфигурации. Он предоставляет расширенный функционал по сравнению с подходом на основе файлов конфигурации, описанным выше, и является рекомендуемым методом настройки для новых приложений и развертываний. Поскольку для хранения информации о конфигурации используется словарь Python, и вы можете заполнять этот словарь различными способами, у вас появляется больше возможностей для настройки. Например, вы можете использовать файл конфигурации в формате JSON или, если у вас есть доступ к функциям обработки YAML, файл в формате YAML для заполнения словаря конфигурации. Или, конечно, вы можете создать словарь в коде Python, получить его в сериализованном виде через сокет или использовать любой другой подход, подходящий для вашего приложения.434435Вот пример той же конфигурации, что и выше, в формате YAML для нового подхода на основе словарей:436437```yaml438version: 1439formatters:440  simple:441    format: '%(asctime)s - %(name)s - %(levelname)s - %(message)s'442handlers:443  console:444    class: logging.StreamHandler445    level: DEBUG446    formatter: simple447    stream: ext://sys.stdout448loggers:449  simpleExample:450    level: DEBUG451    handlers: [console]452    propagate: no453root:454  level: DEBUG455  handlers: [console]456```457458Для получения дополнительной информации о журналировании с использованием словаря см. [функции конфигурации](https://python-all.ru/3.12/library/logging.config.html#logging-config-api).459460### Что произойдет, если конфигурация не предоставлена461462Если конфигурация журналирования не предоставлена, может возникнуть ситуация, когда требуется вывести событие журналирования, но не найдено ни одного обработчика для его вывода.463464Событие выводится с помощью «обработчика последней надежды», хранящегося в [`lastResort`](https://python-all.ru/3.12/library/logging.html#logging.lastResort). Этот внутренний обработчик не связан ни с одним логгером и действует как [`StreamHandler`](https://python-all.ru/3.12/library/logging.handlers.html#logging.StreamHandler), который записывает сообщение с описанием события в текущее значение `sys.stderr` (таким образом, учитывая любые перенаправления, которые могут быть активны). Сообщение не форматируется – печатается только само описание события. Уровень обработчика установлен на `WARNING`, поэтому все события этого и более высоких уровней серьезности будут выводиться.465466Изменено в версии 3.2: Для версий Python до 3.2 поведение следующее:467468- Если [`raiseExceptions`](https://python-all.ru/3.12/library/logging.html#logging.raiseExceptions) равен `False` (рабочий режим), событие молча отбрасывается.469- Если [`raiseExceptions`](https://python-all.ru/3.12/library/logging.html#logging.raiseExceptions) равен `True` (режим разработки), один раз выводится сообщение «Не найдено обработчиков для логгера X.Y.Z».470471Чтобы получить поведение до версии 3.2, [`lastResort`](https://python-all.ru/3.12/library/logging.html#logging.lastResort) может быть установлен в `None`.472473### Настройка журналирования для библиотеки474475При разработке библиотеки, использующей журналирование, следует позаботиться о документировании того, как библиотека использует журналирование – например, имена используемых логгеров. Также необходимо уделить внимание конфигурации журналирования. Если использующее приложение не использует журналирование, а код библиотеки делает вызовы журналирования, то (как описано в предыдущем разделе) события уровня `WARNING` и выше будут выводиться в `sys.stderr`. Это считается наилучшим поведением по умолчанию.476477Если по какой-то причине вы *не* хотите, чтобы эти сообщения печатались при отсутствии конфигурации журналирования, вы можете прикрепить пустой обработчик к корневому логгеру вашей библиотеки. Это предотвратит печать сообщения, так как для событий библиотеки всегда будет найден обработчик: он просто не выдает никакого вывода. Если пользователь библиотеки настраивает журналирование для приложения, предполагается, что эта конфигурация добавит несколько обработчиков, и если уровни настроены соответствующим образом, то вызовы журналирования из кода библиотеки будут отправлять вывод на эти обработчики, как обычно.478479В пакете logging есть пустой обработчик: [`NullHandler`](https://python-all.ru/3.12/library/logging.handlers.html#logging.NullHandler) (начиная с Python 3.1). Экземпляр этого обработчика можно добавить к корневому логгеру пространства имен журналирования, используемого библиотекой (*если* вы хотите предотвратить вывод зарегистрированных событий вашей библиотеки в `sys.stderr` при отсутствии конфигурации журналирования). Если все журналирование библиотеки *foo* выполняется с использованием логгеров с именами вида 'foo.x', 'foo.x.y' и т.д., то код:480481```python482import logging483logging.getLogger('foo').addHandler(logging.NullHandler())484```485486должен дать желаемый эффект. Если организация выпускает несколько библиотек, то имя логгера может быть 'orgname.foo', а не просто 'foo'.487488> **Примечание**489>490> It is strongly advised that you *do not log to the root logger* in your library. Instead, use a logger with a unique and easily identifiable name, such as the `__name__` for your library’s top-level package or module. Logging to the root logger will make it difficult or impossible for the application developer to configure the logging verbosity or handlers of your library as they wish.491492> **Примечание**493>494> It is strongly advised that you *do not add any handlers other than* [`NullHandler`](https://python-all.ru/3.12/library/logging.handlers.html#logging.NullHandler) *to your library’s loggers*. This is because the configuration of handlers is the prerogative of the application developer who uses your library. The application developer knows their target audience and what handlers are most appropriate for their application: if you add handlers ‘under the hood’, you might well interfere with their ability to carry out unit tests and deliver logs which suit their requirements.495496## Уровни журналирования497498Числовые значения уровней журналирования приведены в следующей таблице. Они представляют интерес в первую очередь, если вы хотите определить собственные уровни и хотите, чтобы они имели определенные значения относительно предопределенных уровней. Если вы определяете уровень с тем же числовым значением, он перезаписывает предопределенное значение; предопределенное имя теряется.499500| Уровень | Числовое значение |501| --- | --- |502| `CRITICAL` | 50 |503| `ERROR` | 40 |504| `WARNING` | 30 |505| `INFO` | 20 |506| `DEBUG` | 10 |507| `NOTSET` | 0 |508509Уровни также могут быть связаны с логгерами: они устанавливаются разработчиком или загружаются из сохраненной конфигурации журналирования. Когда на логгере вызывается метод журналирования, логгер сравнивает свой собственный уровень с уровнем, связанным с вызовом метода. Если уровень логгера выше уровня вызова метода, сообщение журналирования фактически не создается. Это базовый механизм, управляющий подробностью вывода журналирования.510511Сообщения журналирования кодируются как экземпляры класса [`LogRecord`](https://python-all.ru/3.12/library/logging.html#logging.LogRecord). Когда логгер решает действительно зарегистрировать событие, из сообщения журналирования создается экземпляр [`LogRecord`](https://python-all.ru/3.12/library/logging.html#logging.LogRecord).512513Logging messages are subjected to a dispatch mechanism through the use of *handlers*, which are instances of subclasses of the [`Handler`](https://python-all.ru/3.12/library/logging.html#logging.Handler) class. Handlers are responsible for ensuring that a logged message (in the form of a [`LogRecord`](https://python-all.ru/3.12/library/logging.html#logging.LogRecord)) ends up in a particular location (or set of locations) which is useful for the target audience for that message (such as end users, support desk staff, system administrators, developers). Handlers are passed [`LogRecord`](https://python-all.ru/3.12/library/logging.html#logging.LogRecord) instances intended for particular destinations. Each logger can have zero, one or more handlers associated with it (via the [`addHandler()`](https://python-all.ru/3.12/library/logging.html#logging.Logger.addHandler) method of [`Logger`](https://python-all.ru/3.12/library/logging.html#logging.Logger)). In addition to any handlers directly associated with a logger, *all handlers associated with all ancestors of the logger* are called to dispatch the message (unless the *propagate* flag for a logger is set to a false value, at which point the passing to ancestor handlers stops).514515Как и у логгеров, у обработчиков могут быть связанные с ними уровни. Уровень обработчика действует как фильтр так же, как и уровень логгера. Если обработчик решает на самом деле отправить событие, используется метод [`emit()`](https://python-all.ru/3.12/library/logging.html#logging.Handler.emit) для отправки сообщения по назначению. Большинству пользовательских подклассов [`Handler`](https://python-all.ru/3.12/library/logging.html#logging.Handler) потребуется переопределить этот [`emit()`](https://python-all.ru/3.12/library/logging.html#logging.Handler.emit).516517### Пользовательские уровни518519Определение собственных уровней возможно, но обычно в этом нет необходимости, так как существующие уровни были выбраны на основе практического опыта. Однако если вы уверены, что вам нужны пользовательские уровни, делайте это с большой осторожностью, и это, возможно, *очень плохая идея определять пользовательские уровни, если вы разрабатываете библиотеку*. Это связано с тем, что если несколько авторов библиотек определят свои собственные пользовательские уровни, то вывод журнала от таких нескольких библиотек, используемых вместе, будет трудно контролировать и/или интерпретировать разработчику-пользователю, так как одно и то же числовое значение может означать разные вещи для разных библиотек.520521## Полезные обработчики522523В дополнение к базовому классу [`Handler`](https://python-all.ru/3.12/library/logging.html#logging.Handler) предоставляется множество полезных подклассов:5245251. Экземпляры [`StreamHandler`](https://python-all.ru/3.12/library/logging.handlers.html#logging.StreamHandler) отправляют сообщения в потоки данных (файлоподобные объекты).5262. Экземпляры [`FileHandler`](https://python-all.ru/3.12/library/logging.handlers.html#logging.FileHandler) отправляют сообщения в файлы на диске.5273. [`BaseRotatingHandler`](https://python-all.ru/3.12/library/logging.handlers.html#logging.handlers.BaseRotatingHandler) – это базовый класс для обработчиков, которые выполняют ротацию файлов журнала в определенный момент. Он не предназначен для прямого создания экземпляров. Вместо этого используйте [`RotatingFileHandler`](https://python-all.ru/3.12/library/logging.handlers.html#logging.handlers.RotatingFileHandler) или [`TimedRotatingFileHandler`](https://python-all.ru/3.12/library/logging.handlers.html#logging.handlers.TimedRotatingFileHandler).5284. Экземпляры [`RotatingFileHandler`](https://python-all.ru/3.12/library/logging.handlers.html#logging.handlers.RotatingFileHandler) отправляют сообщения в файлы на диске с поддержкой максимального размера файлов журнала и ротации файлов журнала.5295. Экземпляры [`TimedRotatingFileHandler`](https://python-all.ru/3.12/library/logging.handlers.html#logging.handlers.TimedRotatingFileHandler) отправляют сообщения в файлы на диске, выполняя ротацию файла журнала через определенные интервалы времени.5306. Экземпляры [`SocketHandler`](https://python-all.ru/3.12/library/logging.handlers.html#logging.handlers.SocketHandler) отправляют сообщения в сокеты TCP/IP. Начиная с версии 3.4 также поддерживаются сокеты домена Unix.5317. Экземпляры [`DatagramHandler`](https://python-all.ru/3.12/library/logging.handlers.html#logging.handlers.DatagramHandler) отправляют сообщения в сокеты UDP. Начиная с версии 3.4 также поддерживаются сокеты домена Unix.5328. Экземпляры [`SMTPHandler`](https://python-all.ru/3.12/library/logging.handlers.html#logging.handlers.SMTPHandler) отправляют сообщения на указанный адрес электронной почты.5339. Экземпляры [`SysLogHandler`](https://python-all.ru/3.12/library/logging.handlers.html#logging.handlers.SysLogHandler) отправляют сообщения демону syslog в Unix, возможно, на удаленной машине.53410. Экземпляры [`NTEventLogHandler`](https://python-all.ru/3.12/library/logging.handlers.html#logging.handlers.NTEventLogHandler) отправляют сообщения в журнал событий Windows NT/2000/XP.53511. Экземпляры [`MemoryHandler`](https://python-all.ru/3.12/library/logging.handlers.html#logging.handlers.MemoryHandler) отправляют сообщения в буфер в памяти, который сбрасывается при выполнении определенных условий.53612. Экземпляры [`HTTPHandler`](https://python-all.ru/3.12/library/logging.handlers.html#logging.handlers.HTTPHandler) отправляют сообщения на HTTP-сервер, используя семантику `GET` или `POST`.53713. Экземпляры [`WatchedFileHandler`](https://python-all.ru/3.12/library/logging.handlers.html#logging.handlers.WatchedFileHandler) следят за файлом, в который они ведут журнал. Если файл изменяется, он закрывается и открывается заново с использованием того же имени файла. Этот обработчик полезен только в Unix-подобных системах; Windows не поддерживает используемый механизм.53814. Экземпляры [`QueueHandler`](https://python-all.ru/3.12/library/logging.handlers.html#logging.handlers.QueueHandler) отправляют сообщения в очередь, например, такие, как реализованные в модулях [`queue`](https://python-all.ru/3.12/library/queue.html#module-queue) или [`multiprocessing`](https://python-all.ru/3.12/library/multiprocessing.html#module-multiprocessing).53915. Экземпляры [`NullHandler`](https://python-all.ru/3.12/library/logging.handlers.html#logging.NullHandler) ничего не делают с сообщениями об ошибках. Они используются разработчиками библиотек, которые хотят использовать журналирование, но хотят избежать сообщения «Не найдено обработчиков для логгера *XXX*», которое может отображаться, если пользователь библиотеки не настроил журналирование. Дополнительную информацию см. в разделе [Настройка журналирования для библиотеки](https://python-all.ru/3.12/howto/logging.html#library-config).540541Добавлено в версии 3.1: Класс [`NullHandler`](https://python-all.ru/3.12/library/logging.handlers.html#logging.NullHandler).542543Добавлено в версии 3.2: Класс [`QueueHandler`](https://python-all.ru/3.12/library/logging.handlers.html#logging.handlers.QueueHandler).544545Классы [`NullHandler`](https://python-all.ru/3.12/library/logging.handlers.html#logging.NullHandler), [`StreamHandler`](https://python-all.ru/3.12/library/logging.handlers.html#logging.StreamHandler) и [`FileHandler`](https://python-all.ru/3.12/library/logging.handlers.html#logging.FileHandler) определены в основном пакете logging. Остальные обработчики определены в подмодуле [`logging.handlers`](https://python-all.ru/3.12/library/logging.handlers.html#module-logging.handlers). (Также существует еще один подмодуль, [`logging.config`](https://python-all.ru/3.12/library/logging.config.html#module-logging.config), для функциональности конфигурации.)546547Записанные сообщения форматируются для представления через экземпляры класса [`Formatter`](https://python-all.ru/3.12/library/logging.html#logging.Formatter). Они инициализируются строкой форматирования, подходящей для использования с оператором % и словарем.548549Для форматирования нескольких сообщений в пакете можно использовать экземпляры [`BufferingFormatter`](https://python-all.ru/3.12/library/logging.html#logging.BufferingFormatter). В дополнение к строке форматирования (которая применяется к каждому сообщению в пакете) предусмотрены строки форматирования заголовка и окончания.550551Когда фильтрации на основе уровня логгера и/или уровня обработчика недостаточно, экземпляры [`Filter`](https://python-all.ru/3.12/library/logging.html#logging.Filter) могут быть добавлены как к экземплярам [`Logger`](https://python-all.ru/3.12/library/logging.html#logging.Logger), так и [`Handler`](https://python-all.ru/3.12/library/logging.html#logging.Handler) (через их метод [`addFilter()`](https://python-all.ru/3.12/library/logging.html#logging.Handler.addFilter)). Прежде чем принять решение о дальнейшей обработке сообщения, и логгеры, и обработчики запрашивают разрешение у всех своих фильтров. Если любой фильтр возвращает ложное значение, сообщение не обрабатывается дальше.552553Базовая функциональность [`Filter`](https://python-all.ru/3.12/library/logging.html#logging.Filter) позволяет фильтровать по конкретному имени логгера. Если эта функция используется, сообщения, отправленные указанному логгеру и его дочерним элементам, пропускаются через фильтр, а все остальные отбрасываются.554555## Исключения, возникшие во время журналирования556557Пакет logging спроектирован таким образом, чтобы проглатывать исключения, возникающие при журналировании в рабочей среде. Это делается для того, чтобы ошибки, возникающие при обработке событий журналирования – такие как неверная конфигурация журналирования, сетевые или другие подобные ошибки – не приводили к преждевременному завершению приложения, использующего журналирование.558559Исключения [`SystemExit`](https://python-all.ru/3.12/library/exceptions.html#SystemExit) и [`KeyboardInterrupt`](https://python-all.ru/3.12/library/exceptions.html#KeyboardInterrupt) никогда не проглатываются. Другие исключения, возникающие во время выполнения метода [`emit()`](https://python-all.ru/3.12/library/logging.html#logging.Handler.emit) подкласса [`Handler`](https://python-all.ru/3.12/library/logging.html#logging.Handler), передаются его методу [`handleError()`](https://python-all.ru/3.12/library/logging.html#logging.Handler.handleError).560561Реализация [`handleError()`](https://python-all.ru/3.12/library/logging.html#logging.Handler.handleError) по умолчанию в [`Handler`](https://python-all.ru/3.12/library/logging.html#logging.Handler) проверяет, установлена ли переменная уровня модуля [`raiseExceptions`](https://python-all.ru/3.12/library/logging.html#logging.raiseExceptions). Если установлена, обратная трассировка выводится в [`sys.stderr`](https://python-all.ru/3.12/library/sys.html#sys.stderr). Если не установлена, исключение проглатывается.562563> **Примечание**564>565> Значение по умолчанию для [`raiseExceptions`](https://python-all.ru/3.12/library/logging.html#logging.raiseExceptions) – `True`. Это связано с тем, что в процессе разработки обычно требуется получать уведомления о любых возникающих исключениях. Рекомендуется установить [`raiseExceptions`](https://python-all.ru/3.12/library/logging.html#logging.raiseExceptions) в `False` для рабочего использования.566567## Использование произвольных объектов в качестве сообщений568569В предыдущих разделах и примерах предполагалось, что сообщение, передаваемое при логировании события, является строкой. Однако это не единственная возможность. Можно передавать произвольный объект в качестве сообщения, и его метод [`__str__()`](https://python-all.ru/3.12/reference/datamodel.html#object.__str__) будет вызван, когда системе логирования потребуется преобразовать его в строковое представление. Фактически, при желании можно вообще избежать вычисления строкового представления – например, [`SocketHandler`](https://python-all.ru/3.12/library/logging.handlers.html#logging.handlers.SocketHandler) отправляет событие, сериализуя его и передавая по сети.570571## Оптимизация572573Форматирование аргументов сообщения откладывается до тех пор, пока его нельзя избежать. Однако вычисление аргументов, передаваемых методу логирования, также может быть затратным, и, возможно, стоит избегать этого, если логгер всё равно отбросит ваше событие. Чтобы принять решение, можно вызвать метод [`isEnabledFor()`](https://python-all.ru/3.12/library/logging.html#logging.Logger.isEnabledFor), который принимает аргумент уровня и возвращает true, если событие будет создано Логгером для этого уровня вызова. Можно написать такой код:574575```python576if logger.isEnabledFor(logging.DEBUG):577    logger.debug('Message with %s, %s', expensive_func1(),578                                        expensive_func2())579```580581так что если порог логгера установлен выше `DEBUG`, вызовы `expensive_func1` и `expensive_func2` никогда не выполняются.582583> **Примечание**584>585> В некоторых случаях сам [`isEnabledFor()`](https://python-all.ru/3.12/library/logging.html#logging.Logger.isEnabledFor) может быть более затратным, чем хотелось бы (например, для глубоко вложенных логгеров, где явный уровень установлен только высоко в иерархии логгеров). В таких случаях (или если нужно избегать вызова метода в тесных циклах) можно кэшировать результат вызова [`isEnabledFor()`](https://python-all.ru/3.12/library/logging.html#logging.Logger.isEnabledFor) в локальной переменной или атрибуте экземпляра и использовать его вместо вызова метода каждый раз. Такое кэшированное значение потребуется пересчитывать только при динамическом изменении конфигурации логирования во время работы приложения (что бывает не так часто).586587Существуют и другие оптимизации, которые можно выполнить для конкретных приложений, требующих более точного контроля над тем, какая информация логирования собирается. Вот список действий, которые можно предпринять, чтобы избежать ненужной обработки во время логирования:588589| Что не нужно собирать | Как избежать сбора |590| --- | --- |591| Информация о том, откуда были сделаны вызовы. | Установите `logging._srcfile` в `None`. Это позволяет избежать вызова [`sys._getframe()`](https://python-all.ru/3.12/library/sys.html#sys._getframe), что может помочь ускорить код в таких средах, как PyPy (который не может ускорить код, использующий [`sys._getframe()`](https://python-all.ru/3.12/library/sys.html#sys._getframe)). |592| Информация о потоках. | Устанавливает `logging.logThreads` в `False`. |593| Идентификатор текущего процесса ([`os.getpid()`](https://python-all.ru/3.12/library/os.html#os.getpid)) | Устанавливает `logging.logProcesses` в `False`. |594| Имя текущего процесса при использовании `multiprocessing` для управления несколькими процессами. | Устанавливает `logging.logMultiprocessing` в `False`. |595| Имя текущего [`asyncio.Task`](https://python-all.ru/3.12/library/asyncio-task.html#asyncio.Task) при использовании `asyncio`. | Устанавливает `logging.logAsyncioTasks` в `False`. |596597Также обратите внимание, что ядро модуля logging включает только базовые обработчики. Если не импортировать [`logging.handlers`](https://python-all.ru/3.12/library/logging.handlers.html#module-logging.handlers) и [`logging.config`](https://python-all.ru/3.12/library/logging.config.html#module-logging.config), они не будут занимать память.598599## Другие ресурсы600601> **См. также**602>603> **Модуль [`logging`](https://python-all.ru/3.12/library/logging.html#module-logging)**604>605> Справочник API для модуля logging.606>607> **Модуль [`logging.config`](https://python-all.ru/3.12/library/logging.config.html#module-logging.config)**608>609> API конфигурации для модуля logging.610>611> **Модуль [`logging.handlers`](https://python-all.ru/3.12/library/logging.handlers.html#module-logging.handlers)**612>613> Полезные обработчики, входящие в состав модуля logging.614>615> [Кулинарная книга по логированию](https://python-all.ru/3.12/howto/logging-cookbook.html#logging-cookbook)616