logging.config.md
1> **Источник:** https://python-all.ru/3.16/library/logging.config.html2>3> «Документация Python на русском» – неофициальный перевод официальной документации Python: версии от 2.6 до 3.16, полнотекстовый поиск, английский оригинал рядом с переводом. Эта Markdown-версия страницы предназначена для работы с LLM: вставьте её в ChatGPT, Claude или Cursor.45---67# `logging.config` – Конфигурация логирования89**Исходный код:** [Lib/logging/config.py](https://python-all.ru/src/main/Lib/logging/config.py)1011Важно1213На этой странице представлена только справочная информация. Учебные руководства см. в1415- [Базовое руководство](https://python-all.ru/3.16/howto/logging.html#logging-basic-tutorial)16- [Продвинутое руководство](https://python-all.ru/3.16/howto/logging.html#logging-advanced-tutorial)17- [Поваренная книга по логированию](https://python-all.ru/3.16/howto/logging-cookbook.html#logging-cookbook)1819---2021В этом разделе описывается API для настройки модуля logging.2223## Функции настройки2425Следующие функции настраивают модуль logging. Они находятся в модуле `logging.config`. Их использование не обязательно – модуль logging можно настраивать с помощью этих функций или напрямую обращаясь к основному API (определённому в самом [`logging`](https://python-all.ru/3.16/library/logging.html#module-logging)), а также определяя обработчики, объявленные в `logging` или [`logging.handlers`](https://python-all.ru/3.16/library/logging.handlers.html#module-logging.handlers).2627#### `logging.config.dictConfig(config)`2829Принимает конфигурацию логирования из словаря. Содержимое этого словаря описано ниже в разделе [Схема словаря конфигурации](https://python-all.ru/3.16/library/logging.config.html#logging-config-dictschema).3031Если во время настройки возникает ошибка, эта функция возбуждает [`ValueError`](https://python-all.ru/3.16/library/exceptions.html#ValueError), [`TypeError`](https://python-all.ru/3.16/library/exceptions.html#TypeError), [`AttributeError`](https://python-all.ru/3.16/library/exceptions.html#AttributeError) или [`ImportError`](https://python-all.ru/3.16/library/exceptions.html#ImportError) с соответствующим описанием. Ниже приведён (возможно, неполный) перечень условий, при которых возникает ошибка:3233- Значение `level`, не являющееся строкой или являющееся строкой, не соответствующей ни одному реальному уровню логирования.34- Значение `propagate`, не являющееся булевым.35- Идентификатор, для которого нет соответствующего получателя.36- Идентификатор несуществующего обработчика, обнаруженный во время инкрементального вызова.37- Некорректное имя логгера.38- Невозможность разрешить ссылку на внутренний или внешний объект.3940Разбор выполняется классом `DictConfigurator`, конструктору которого передаётся словарь конфигурации; этот класс имеет метод `configure()`. В модуле `logging.config` есть вызываемый атрибут `dictConfigClass`, изначально установленный в `DictConfigurator`. Вы можете заменить значение `dictConfigClass` на собственную подходящую реализацию.4142`dictConfig()` вызывает `dictConfigClass`, передавая указанный словарь, а затем вызывает метод `configure()` возвращённого объекта, чтобы применить конфигурацию:4344```python45def dictConfig(config):46 dictConfigClass(config).configure()47```4849Например, подкласс `DictConfigurator` может вызывать `DictConfigurator.__init__()` в собственном методе `__init__()`, а затем задать пользовательские префиксы, которые будут использоваться при последующем вызове `configure()`. `dictConfigClass` будет привязан к этому новому подклассу, и `dictConfig()` можно будет вызывать так же, как и в состоянии по умолчанию, без настройки.5051Добавлено в версии 3.2.5253#### `logging.config.fileConfig(fname, defaults=None, disable_existing_loggers=True, encoding=None)`5455Читает конфигурацию логирования из файла в формате [`configparser`](https://python-all.ru/3.16/library/configparser.html#module-configparser). Формат файла должен соответствовать описанию в разделе [Формат конфигурационного файла](https://python-all.ru/3.16/library/logging.config.html#logging-config-fileformat). Эту функцию можно вызывать несколько раз в приложении, что позволяет конечному пользователю выбирать из нескольких готовых конфигураций (если разработчик предоставляет механизм для выбора и загрузки выбранной конфигурации).5657Возбуждает [`FileNotFoundError`](https://python-all.ru/3.16/library/exceptions.html#FileNotFoundError), если файл не существует, и [`RuntimeError`](https://python-all.ru/3.16/library/exceptions.html#RuntimeError), если файл некорректен или пуст.5859**Параметры:**6061- **fname** – имя файла, или файлоподобный объект, или экземпляр, производный от [`RawConfigParser`](https://python-all.ru/3.16/library/configparser.html#configparser.RawConfigParser). Если передан экземпляр, производный от `RawConfigParser`, он используется как есть. В противном случае создаётся [`ConfigParser`](https://python-all.ru/3.16/library/configparser.html#configparser.ConfigParser), и конфигурация считывается им из объекта, переданного в `fname`. Если у этого объекта есть метод [`readline()`](https://python-all.ru/3.16/library/readline.html#module-readline), предполагается, что это файлоподобный объект, и чтение выполняется через [`read_file()`](https://python-all.ru/3.16/library/configparser.html#configparser.ConfigParser.read_file); в противном случае считается, что это имя файла, и оно передаётся в [`read()`](https://python-all.ru/3.16/library/configparser.html#configparser.ConfigParser.read).62- **defaults** – значения по умолчанию, передаваемые в `ConfigParser`, можно указать в этом аргументе.63- **disable\_existing\_loggers** – если указано `False`, логгеры, существующие на момент вызова, остаются включёнными. Значение по умолчанию – `True`, поскольку это обеспечивает обратную совместимость со старым поведением. Это поведение отключает все существующие не корневые логгеры, если только они или их предки явно не указаны в конфигурации логирования.64- **encoding** – кодировка, используемая при открытии файла, когда *fname* является именем файла.6566Изменено в версии 3.4: Экземпляр подкласса [`RawConfigParser`](https://python-all.ru/3.16/library/configparser.html#configparser.RawConfigParser) теперь принимается как значение для `fname`. Это позволяет:6768- Использование конфигурационного файла, где настройка логирования является лишь частью общей конфигурации приложения.69- Использование конфигурации, прочитанной из файла и затем изменённой приложением (например, на основе параметров командной строки или других аспектов среды выполнения) перед передачей в `fileConfig`.7071Изменено в версии 3.10: Добавлен параметр *encoding*.7273Изменено в версии 3.12: Будет возбуждено исключение, если указанный файл не существует, некорректен или пуст.7475#### `logging.config.listen(port=DEFAULT_LOGGING_CONFIG_PORT, verify=None)`7677Запускает сокет-сервер на указанном порту и ожидает новые конфигурации. Если порт не указан, используется значение по умолчанию модуля `DEFAULT_LOGGING_CONFIG_PORT`. Конфигурации логирования будут отправляться в виде файла, пригодного для обработки с помощью [`dictConfig()`](https://python-all.ru/3.16/library/logging.config.html#logging.config.dictConfig) или [`fileConfig()`](https://python-all.ru/3.16/library/logging.config.html#logging.config.fileConfig). Возвращает экземпляр [`Thread`](https://python-all.ru/3.16/library/threading.html#threading.Thread), на котором можно вызвать [`start()`](https://python-all.ru/3.16/library/threading.html#threading.Thread.start) для запуска сервера и который можно [`join()`](https://python-all.ru/3.16/library/threading.html#threading.Thread.join), когда необходимо. Чтобы остановить сервер, вызовите [`stopListening()`](https://python-all.ru/3.16/library/logging.config.html#logging.config.stopListening).7879Аргумент `verify`, если он указан, должен быть вызываемым объектом, который проверяет, являются ли полученные через сокет байты допустимыми и должны ли быть обработаны. Это можно сделать, зашифровав и/или подписав передаваемые через сокет данные, чтобы вызываемый объект `verify` мог выполнить проверку подписи и/или расшифровку. Вызываемый объект `verify` вызывается с одним аргументом – байтами, полученными через сокет, – и должен вернуть байты для обработки или `None`, чтобы указать, что байты следует отбросить. Возвращённые байты могут совпадать с переданными (например, когда выполняется только проверка) или быть совершенно другими (возможно, если была выполнена расшифровка).8081Чтобы отправить конфигурацию в сокет, прочитайте файл конфигурации и отправьте его в сокет в виде последовательности байтов, перед которыми идёт четырёхбайтовая строка длины, упакованная в двоичном виде с помощью `struct.pack('>L', n)`.8283> **Примечание**84>85> Поскольку части конфигурации передаются через [`eval()`](https://python-all.ru/3.16/library/functions.html#eval), использование этой функции может подвергнуть пользователей риску безопасности. Хотя функция привязывается только к сокету на `localhost` и не принимает соединения с удалённых машин, существуют сценарии, при которых недоверенный код может быть выполнен от имени процесса, вызывающего `listen()`. В частности, если процесс, вызывающий `listen()`, работает на многопользовательской машине, где пользователи не доверяют друг другу, то злоумышленник может организовать выполнение практически произвольного кода в процессе жертвы, просто подключившись к сокету `listen()` жертвы и отправив конфигурацию, которая запускает любой код, который хочет выполнить атакующий. Это особенно легко сделать, если используется порт по умолчанию, но не сложно, даже если используется другой порт. Чтобы избежать этого риска, используйте аргумент `verify` для `listen()`, чтобы предотвратить применение нераспознанных конфигураций.8687Изменено в версии 3.4: Добавлен аргумент `verify`.8889> **Примечание**90>91> Если вы хотите отправлять прослушивателю конфигурации, которые не отключают существующие регистраторы, вам нужно использовать формат JSON для конфигурации, который будет использовать [`dictConfig()`](https://python-all.ru/3.16/library/logging.config.html#logging.config.dictConfig) для настройки. Этот метод позволяет указать `disable_existing_loggers` как `False` в отправляемой конфигурации.9293#### `logging.config.stopListening()`9495Останавливает сервер прослушивания, созданный вызовом [`listen()`](https://python-all.ru/3.16/library/logging.config.html#logging.config.listen). Обычно вызывается перед вызовом `join()` для возвращаемого значения из `listen()`.9697## Соображения безопасности9899Функциональность конфигурации логирования старается обеспечить удобство, и отчасти это достигается за счёт возможности преобразовывать текст из файлов конфигурации в объекты Python, используемые в настройке логирования – например, как описано в разделе [Пользовательские объекты](https://python-all.ru/3.16/library/logging.config.html#logging-config-dict-userdef). Однако те же самые механизмы (импорт вызываемых объектов из пользовательских модулей и их вызов с параметрами из конфигурации) могут быть использованы для вызова любого кода, и поэтому следует относиться к файлам конфигурации из ненадёжных источников с *крайней осторожностью* и убедиться, что ничего плохого не произойдёт при их загрузке, перед фактической загрузкой.100101## Схема словаря конфигурации102103Описание конфигурации логирования требует перечисления различных создаваемых объектов и связей между ними; например, можно создать обработчик с именем «console», а затем указать, что регистратор с именем «startup» будет отправлять свои сообщения обработчику «console». Эти объекты не ограничиваются теми, что предоставляются модулем [`logging`](https://python-all.ru/3.16/library/logging.html#module-logging), поскольку можно написать собственный класс форматтера или обработчика. Параметры этих классов также могут включать внешние объекты, такие как `sys.stderr`. Синтаксис для описания этих объектов и связей определён ниже в разделе [Связи объектов](https://python-all.ru/3.16/library/logging.config.html#logging-config-dict-connections).104105### Подробности схемы словаря106107Словарь, передаваемый [`dictConfig()`](https://python-all.ru/3.16/library/logging.config.html#logging.config.dictConfig), должен содержать следующие ключи:108109- *version* – должно быть установлено целочисленное значение, представляющее версию схемы. Единственное допустимое значение на данный момент – 1, но наличие этого ключа позволяет схеме развиваться, сохраняя обратную совместимость.110111Все остальные ключи необязательны, но если они присутствуют, то будут интерпретироваться, как описано ниже. Во всех случаях, когда ниже упоминается «настраивающий словарь», в нём будет проверяться наличие специального ключа `'()'`, чтобы определить, требуется ли пользовательское создание экземпляра. Если да, то используется механизм, описанный ниже в разделе [Пользовательские объекты](https://python-all.ru/3.16/library/logging.config.html#logging-config-dict-userdef), для создания экземпляра; в противном случае контекст используется для определения того, что создавать.112113- *formatters* – соответствующее значение будет словарём, в котором каждый ключ – это идентификатор форматтера, а каждое значение – словарь, описывающий, как настроить соответствующий экземпляр [`Formatter`](https://python-all.ru/3.16/library/logging.html#logging.Formatter).114115 В настраивающем словаре ищутся следующие необязательные ключи, которые соответствуют аргументам, передаваемым для создания объекта [`Formatter`](https://python-all.ru/3.16/library/logging.html#logging.Formatter):116117 - `format`118 - `datefmt`119 - `style`120 - `validate` (начиная с версии \>=3.8)121 - `defaults` (начиная с версии \>=3.12)122123 Необязательный ключ `class` указывает имя класса форматтера (в виде полного имени модуля и класса). Аргументы для создания экземпляра такие же, как для [`Formatter`](https://python-all.ru/3.16/library/logging.html#logging.Formatter), поэтому этот ключ наиболее полезен для создания экземпляра настраиваемого подкласса `Formatter`. Например, альтернативный класс может отображать трассировки исключений в расширенном или сжатом формате. Если ваш форматтер требует других или дополнительных ключей конфигурации, следует использовать раздел [Пользовательские объекты](https://python-all.ru/3.16/library/logging.config.html#logging-config-dict-userdef).124- *filters* – соответствующее значение будет словарём, в котором каждый ключ – это идентификатор фильтра, а каждое значение – словарь, описывающий, как настроить соответствующий экземпляр Filter.125126 В настраивающем словаре ищется ключ `name` (по умолчанию пустая строка), и он используется для создания экземпляра [`logging.Filter`](https://python-all.ru/3.16/library/logging.html#logging.Filter).127- *handlers* – соответствующее значение будет словарём, в котором каждый ключ – это идентификатор обработчика, а каждое значение – словарь, описывающий, как настроить соответствующий экземпляр Handler.128129 В словаре конфигурации ищутся следующие ключи:130131 - `class` (обязательный). Это полное имя класса обработчика.132 - `level` (необязательный). Уровень обработчика.133 - `formatter` (необязательный). Идентификатор форматтера для этого обработчика.134 - `filters` (необязательный). Список идентификаторов фильтров для этого обработчика.135136 Изменено в версии 3.11: `filters` может принимать экземпляры фильтров в дополнение к идентификаторам.137138 Все *остальные* ключи передаются как именованные аргументы конструктору обработчика. Например, для следующего фрагмента:139140 ```yaml141 handlers:142 console:143 class : logging.StreamHandler144 formatter: brief145 level : INFO146 filters: [allow_foo]147 stream : ext://sys.stdout148 file:149 class : logging.handlers.RotatingFileHandler150 formatter: precise151 filename: logconfig.log152 maxBytes: 1024153 backupCount: 3154 ```155156 обработчик с идентификатором `console` создаётся как [`logging.StreamHandler`](https://python-all.ru/3.16/library/logging.handlers.html#logging.StreamHandler), используя `sys.stdout` в качестве базового потока. Обработчик с идентификатором `file` создаётся как [`logging.handlers.RotatingFileHandler`](https://python-all.ru/3.16/library/logging.handlers.html#logging.handlers.RotatingFileHandler) с именованными аргументами `filename='logconfig.log', maxBytes=1024, backupCount=3`.157- *loggers* – соответствующее значение будет словарём, в котором каждый ключ – это имя регистратора, а каждое значение – словарь, описывающий, как настроить соответствующий экземпляр Logger.158159 В словаре конфигурации ищутся следующие ключи:160161 - `level` (необязательно). Уровень регистратора.162 - `propagate` (необязательно). Настройка распространения записей регистратора.163 - `filters` (необязательно). Список идентификаторов фильтров для этого регистратора.164165 Изменено в версии 3.11: `filters` может принимать экземпляры фильтров в дополнение к идентификаторам.166 - `handlers` (необязательно). Список идентификаторов обработчиков для этого регистратора.167168 Указанные регистраторы будут настроены в соответствии с заданными уровнем, распространением, фильтрами и обработчиками.169- *root* – это конфигурация корневого регистратора. Обработка конфигурации будет такой же, как и для любого другого регистратора, за исключением того, что параметр `propagate` не применяется.170- *incremental* – следует ли интерпретировать конфигурацию как дополнение к существующей. Значение по умолчанию – `False`, что означает, что указанная конфигурация заменяет существующую с той же семантикой, которая используется существующим API [`fileConfig()`](https://python-all.ru/3.16/library/logging.config.html#logging.config.fileConfig).171172 Если указано значение `True`, конфигурация обрабатывается так, как описано в разделе [Incremental Configuration](https://python-all.ru/3.16/library/logging.config.html#logging-config-dict-incremental).173- *disable\_existing\_loggers* – следует ли отключать все существующие некорневые регистраторы. Этот параметр повторяет одноимённый параметр в [`fileConfig()`](https://python-all.ru/3.16/library/logging.config.html#logging.config.fileConfig). Если отсутствует, по умолчанию принимает значение `True`. Это значение игнорируется, если *incremental* равно `True`.174175### Инкрементальная конфигурация176177Обеспечить полную гибкость для инкрементальной конфигурации сложно. Например, поскольку такие объекты, как фильтры и форматировщики, анонимны, после настройки невозможно сослаться на эти анонимные объекты при дополнении конфигурации.178179Кроме того, нет убедительных причин произвольно изменять граф объектов регистраторов, обработчиков, фильтров и форматировщиков во время выполнения после настройки: уровень подробности регистрации можно контролировать просто установкой уровней (а для регистраторов – флагов распространения). Произвольное изменение графа объектов безопасным способом проблематично в многопоточной среде; хотя это возможно, выгода не стоит сложности, которую это вносит в реализацию.180181Поэтому, когда ключ `incremental` словаря конфигурации присутствует и равен `True`, система полностью игнорирует все записи `formatters` и `filters` и обрабатывает только настройки `level` в записях `handlers`, а также настройки `level` и `propagate` в записях `loggers` и `root`.182183Использование значения в словаре конфигурации позволяет отправлять конфигурации по сети в виде сериализованных словарей в сокетный слушатель. Таким образом, уровень подробности регистрации долго работающего приложения можно изменять со временем без необходимости останавливать и перезапускать приложение.184185### Соединения объектов186187Схема описывает набор объектов логирования – регистраторы, обработчики, форматировщики, фильтры – которые соединены друг с другом в граф объектов. Таким образом, схема должна представлять соединения между объектами. Например, предположим, что после настройки к определённому регистратору прикреплён определённый обработчик. В рамках данного обсуждения можно сказать, что регистратор представляет источник, а обработчик – приёмник соединения между ними. Конечно, в настроенных объектах это реализовано тем, что регистратор хранит ссылку на обработчик. В словаре конфигурации это делается путём присвоения каждому объекту- приёмнику идентификатора, однозначно его определяющего, а затем использования этого идентификатора в конфигурации объекта-источника для указания наличия соединения между источником и объектом-приёмником с этим идентификатором.188189Например, рассмотрим следующий фрагмент YAML:190191```yaml192formatters:193 brief:194 # здесь задаётся конфигурация форматтера с id 'brief'195 precise:196 # здесь задаётся конфигурация форматтера с id 'precise'197handlers:198 h1: #Это идентификатор199 # здесь задаётся конфигурация обработчика с id 'h1'200 formatter: brief201 h2: #Это другой идентификатор202 # здесь задаётся конфигурация обработчика с id 'h2'203 formatter: precise204loggers:205 foo.bar.baz:206 # прочая конфигурация для логгера 'foo.bar.baz'207 handlers: [h1, h2]208```209210(Примечание: YAML используется здесь, потому что он немного более читаем, чем эквивалентная форма исходного кода Python для словаря.)211212Идентификаторами для регистраторов являются имена регистраторов, которые использовались бы программно для получения ссылки на эти регистраторы, например, `foo.bar.baz`. Идентификаторы для форматировщиков и фильтров могут быть любыми строковыми значениями (такими как `brief`, `precise` выше) и являются временными – они имеют смысл только для обработки словаря конфигурации и используются для определения связей между объектами, и нигде не сохраняются после завершения вызова конфигурации.213214Указанный выше фрагмент показывает, что к регистратору с именем `foo.bar.baz` должны быть прикреплены два обработчика, которые описываются идентификаторами обработчиков `h1` и `h2`. Форматировщик для `h1` – это тот, который описывается идентификатором `brief`, а форматировщик для `h2` – тот, который описывается идентификатором `precise`.215216### Пользовательские объекты217218Схема поддерживает пользовательские объекты для обработчиков, фильтров и форматировщиков. (Регистраторам не нужны разные типы для разных экземпляров, поэтому в этой схеме конфигурации нет поддержки пользовательских классов регистраторов.)219220Настраиваемые объекты описываются словарями, которые детализируют их конфигурацию. В некоторых местах система логирования сможет определить из контекста, как создать экземпляр объекта, но когда требуется создать экземпляр пользовательского объекта, система не будет знать, как это сделать. Для обеспечения полной гибкости при создании пользовательских объектов пользователь должен предоставить «фабрику» – вызываемый объект, который вызывается со словарём конфигурации и возвращает созданный экземпляр. Это обозначается абсолютным путём импорта к фабрике, доступным под специальным ключом `'()'`. Вот конкретный пример:221222```yaml223formatters:224 brief:225 format: '%(message)s'226 default:227 format: '%(asctime)s %(levelname)-8s %(name)-15s %(message)s'228 datefmt: '%Y-%m-%d %H:%M:%S'229 custom:230 (): my.package.customFormatterFactory231 bar: baz232 spam: 99.9233 answer: 42234```235236Приведённый выше фрагмент YAML определяет три форматировщика. Первый, с идентификатором `brief`, является стандартным экземпляром [`logging.Formatter`](https://python-all.ru/3.16/library/logging.html#logging.Formatter) с указанной строкой формата. Второй, с идентификатором `default`, имеет более длинный формат, а также явно задаёт формат времени, и в результате будет создан экземпляр `logging.Formatter`, инициализированный этими двумя строками формата. В форме исходного кода Python под словари конфигурации форматировщиков `brief` и `default` выглядят так:237238```python239{240 'format' : '%(message)s'241}242```243244и:245246```python247{248 'format' : '%(asctime)s %(levelname)-8s %(name)-15s %(message)s',249 'datefmt' : '%Y-%m-%d %H:%M:%S'250}251```252253соответственно, и поскольку эти словари не содержат специального ключа `'()'`, создание экземпляра выводится из контекста: в результате создаются стандартные экземпляры [`logging.Formatter`](https://python-all.ru/3.16/library/logging.html#logging.Formatter). Подсловарь конфигурации для третьего форматировщика, с идентификатором `custom`, таков:254255```python256{257 '()' : 'my.package.customFormatterFactory',258 'bar' : 'baz',259 'spam' : 99.9,260 'answer' : 42261}262```263264и он содержит специальный ключ `'()'`, что означает необходимость пользовательского создания экземпляра. В этом случае будет использована указанная фабрика-вызываемый объект. Если это реальный вызываемый объект, он будет использован напрямую; в противном случае, если указана строка (как в примере), фактический вызываемый объект будет найден с помощью обычных механизмов импорта. Вызываемый объект будет вызван с **оставшимися** элементами в подсловаре конфигурации в качестве именованных аргументов. В приведённом выше примере предполагается, что форматировщик с идентификатором `custom` будет возвращён вызовом:265266```python267my.package.customFormatterFactory(bar='baz', spam=99.9, answer=42)268```269270> **Предупреждение**271>272> Значения ключей, таких как `bar`, `spam` и `answer` в приведённом выше примере, не должны быть словарями конфигурации или ссылками типа `cfg://foo` или `ext://bar`, потому что они не будут обработаны механизмом конфигурации, а будут переданы вызываемому объекту как есть.273274Ключ `'()'` был выбран в качестве специального, потому что он не является допустимым именем параметра ключевого слова и, следовательно, не будет конфликтовать с именами именованных аргументов, используемых при вызове. `'()'` также служит мнемоническим напоминанием о том, что соответствующее значение является вызываемым объектом.275276Изменено в версии 3.11: Элемент `filters` в `handlers` и `loggers` может принимать экземпляры фильтров в дополнение к идентификаторам.277278Можно также указать специальный ключ `'.'`, значением которого является отображение имён атрибутов в значения. Если такой ключ найден, указанные атрибуты будут установлены на пользовательском объекте перед его возвратом. Так, при следующей конфигурации:279280```python281{282 '()' : 'my.package.customFormatterFactory',283 'bar' : 'baz',284 'spam' : 99.9,285 'answer' : 42,286 '.' {287 'foo': 'bar',288 'baz': 'bozz'289 }290}291```292293возвращаемый форматировщик будет иметь атрибут `foo` равный `'bar'` и атрибут `baz` равный `'bozz'`.294295> **Предупреждение**296>297> Значения таких атрибутов, как `foo` и `baz` в примере выше, не должны быть словарями конфигурации или ссылками вроде `cfg://foo` или `ext://bar`, поскольку они не будут обрабатываться механизмом конфигурации, а будут установлены как значения атрибутов как есть.298299### Порядок конфигурации обработчиков300301Обработчики настраиваются в алфавитном порядке их ключей, и настроенный обработчик заменяет словарь конфигурации в (рабочей копии) словаря `handlers` в схеме. Если используется конструкция вроде `cfg://handlers.foo`, то изначально `handlers['foo']` указывает на словарь конфигурации для обработчика с именем `foo`, а позже (когда этот обработчик будет настроен) он указывает на экземпляр настроенного обработчика. Таким образом, `cfg://handlers.foo` может разрешаться либо в словарь, либо в экземпляр обработчика. В общем случае разумно давать обработчикам такие имена, чтобы зависимые обработчики настраивались *после* любых обработчиков, от которых они зависят; это позволяет использовать конструкцию вроде `cfg://handlers.foo` при настройке обработчика, который зависит от обработчика `foo`. Если бы этот зависимый обработчик был назван `bar`, возникли бы проблемы, поскольку конфигурация `bar` была бы предпринята до конфигурации `foo`, и `foo` ещё не был бы настроен. Однако если бы зависимый обработчик был назван `foobar`, он бы настраивался после `foo`, и в результате `cfg://handlers.foo` разрешалось бы в настроенный обработчик `foo`, а не в его словарь конфигурации.302303### Доступ к внешним объектам304305Бывают случаи, когда конфигурации требуется ссылаться на объекты внешние по отношению к конфигурации, например, `sys.stderr`. Если словарь конфигурации создаётся с помощью кода Python, это несложно, но проблема возникает, когда конфигурация задаётся через текстовый файл (например, JSON, YAML). В текстовом файле нет стандартного способа отличить `sys.stderr` от литеральной строки `'sys.stderr'`. Чтобы облегчить это различие, система конфигурации ищет определённые специальные префиксы в строковых значениях и обрабатывает их особым образом. Например, если литеральная строка `'ext://sys.stderr'` указана как значение в конфигурации, то префикс `ext://` будет отброшен, а оставшаяся часть значения обработана с помощью стандартных механизмов импорта.306307Обработка таких префиксов выполняется аналогично обработке протоколов: существует общий механизм поиска префиксов, соответствующих регулярному выражению `^(?P<prefix>[a-z]+)://(?P<suffix>.*)$`, при котором, если префикс `prefix` распознан, часть `suffix` обрабатывается зависящим от префикса образом, и результат обработки заменяет строковое значение. Если префикс не распознан, строковое значение остаётся без изменений.308309### Доступ к внутренним объектам310311Помимо внешних объектов, иногда также возникает необходимость ссылаться на объекты в самой конфигурации. Это будет делаться неявно системой конфигурации для тех вещей, о которых она знает. Например, строковое значение `'DEBUG'` для `level` в логгере или обработчике будет автоматически преобразовано в значение `logging.DEBUG`, а записи `handlers`, `filters` и `formatter` будут принимать идентификатор объекта и разрешаться в соответствующий целевой объект.312313Однако необходим более общий механизм для пользовательских объектов, которые не известны модулю [`logging`](https://python-all.ru/3.16/library/logging.html#module-logging). Например, рассмотрим [`logging.handlers.MemoryHandler`](https://python-all.ru/3.16/library/logging.handlers.html#logging.handlers.MemoryHandler), который принимает аргумент `target` – другой обработчик для делегирования. Поскольку системе уже известен этот класс, то в конфигурации указанный `target` просто должен быть идентификатором объекта соответствующего целевого обработчика, и система разрешит его в обработчик по идентификатору. Однако если пользователь определяет `my.package.MyHandler`, у которого есть обработчик `alternate`, система конфигурации не будет знать, что `alternate` ссылается на обработчик. Чтобы учесть это, общая система разрешения позволяет пользователю указать:314315```yaml316handlers:317 file:318 # здесь задаётся конфигурация файлового обработчика319320 custom:321 (): my.package.MyHandler322 alternate: cfg://handlers.file323```324325Литеральная строка `'cfg://handlers.file'` будет разрешаться аналогично строкам с префиксом `ext://`, но поиск будет вестись в самой конфигурации, а не в пространстве имён импорта. Этот механизм позволяет доступ по точке или по индексу, подобно тому, как это делается в `str.format`. Таким образом, для следующего фрагмента:326327```yaml328handlers:329 email:330 class: logging.handlers.SMTPHandler331 mailhost: localhost332 fromaddr: my_app@domain.tld333 toaddrs:334 - support_team@domain.tld335 - dev_team@domain.tld336 subject: Houston, we have a problem.337```338339в конфигурации строка `'cfg://handlers'` будет разрешаться в словарь с ключом `handlers`, строка `'cfg://handlers.email` будет разрешаться в словарь с ключом `email` внутри словаря `handlers`, и так далее. Строка `'cfg://handlers.email.toaddrs[1]` будет разрешаться в `'dev_team@domain.tld'`, а строка `'cfg://handlers.email.toaddrs[0]'` – в значение `'support_team@domain.tld'`. Значение `subject` можно получить как через `'cfg://handlers.email.subject'`, так и (эквивалентно) через `'cfg://handlers.email[subject]'`. Вторая форма требуется только если ключ содержит пробелы или неалфавитно-цифровые символы. Обратите внимание, что символы `[` и `]` не допускаются в ключах. Если значение индекса состоит только из десятичных цифр, сначала будет предпринята попытка доступа с использованием соответствующего целочисленного значения, а в случае неудачи – строкового.340341Для строки `cfg://handlers.myhandler.mykey.123` результат будет `config_dict['handlers']['myhandler']['mykey']['123']`. Если строка указана как `cfg://handlers.myhandler.mykey[123]`, система попытается получить значение из `config_dict['handlers']['myhandler']['mykey'][123]`, а в случае неудачи вернётся к `config_dict['handlers']['myhandler']['mykey']['123']`.342343### Разрешение импорта и пользовательские импортёры344345По умолчанию для импорта используется встроенная функция [`__import__()`](https://python-all.ru/3.16/library/functions.html#import__). Возможно, вы захотите заменить её собственным механизмом импорта: в таком случае можно заменить атрибут `importer` класса `DictConfigurator` или его суперкласса, `BaseConfigurator`. Однако нужно быть осторожным из-за того, как функции доступны из классов через дескрипторы. Если вы используете вызываемый объект Python для импорта и хотите определить его на уровне класса, а не экземпляра, необходимо обернуть его в [`staticmethod()`](https://python-all.ru/3.16/library/functions.html#staticmethod). Например:346347```python348from importlib import import_module349from logging.config import BaseConfigurator350351BaseConfigurator.importer = staticmethod(import_module)352```353354Не нужно оборачивать в [`staticmethod()`](https://python-all.ru/3.16/library/functions.html#staticmethod), если вы устанавливаете вызываемый объект импорта на *экземпляре* конфигуратора.355356### Настройка QueueHandler и QueueListener357358Если требуется настроить [`QueueHandler`](https://python-all.ru/3.16/library/logging.handlers.html#logging.handlers.QueueHandler) (учитывая, что он обычно используется вместе с [`QueueListener`](https://python-all.ru/3.16/library/logging.handlers.html#logging.handlers.QueueListener)), можно настроить их совместно. После настройки экземпляр `QueueListener` будет доступен как атрибут [`listener`](https://python-all.ru/3.16/library/logging.handlers.html#logging.handlers.QueueHandler.listener) созданного обработчика, а тот, в свою очередь, будет доступен через [`getHandlerByName()`](https://python-all.ru/3.16/library/logging.html#logging.getHandlerByName) с указанием имени, которое было использовано для `QueueHandler` в конфигурации. Схема словаря для настройки этой пары показана в примере YAML ниже.359360```yaml361handlers:362 qhand:363 class: logging.handlers.QueueHandler364 queue: my.module.queue_factory365 listener: my.package.CustomListener366 handlers:367 - hand_name_1368 - hand_name_2369 ...370```371372Ключи `queue` и `listener` необязательны.373374Если ключ `queue` присутствует, соответствующее значение может быть одним из следующих:375376- Объект, реализующий публичный API [`Queue.put_nowait`](https://python-all.ru/3.16/library/queue.html#queue.Queue.put_nowait) и [`Queue.get`](https://python-all.ru/3.16/library/queue.html#queue.Queue.get). Например, это может быть реальный экземпляр [`queue.Queue`](https://python-all.ru/3.16/library/queue.html#queue.Queue) или его подкласса, либо прокси, полученный с помощью [`multiprocessing.managers.SyncManager.Queue()`](https://python-all.ru/3.16/library/multiprocessing.html#multiprocessing.managers.SyncManager.Queue).377378 Это, конечно, возможно только если вы создаёте или изменяете словарь конфигурации в коде.379- Строка, которая разрешается в вызываемый объект, возвращающий при вызове без аргументов экземпляр очереди для использования. Таким вызываемым объектом может быть подкласс [`queue.Queue`](https://python-all.ru/3.16/library/queue.html#queue.Queue) или функция, возвращающая подходящий экземпляр очереди, например, `my.module.queue_factory()`.380- Словарь с ключом `'()'`, который создаётся обычным способом, как описано в [Пользовательские объекты](https://python-all.ru/3.16/library/logging.config.html#logging-config-dict-userdef). Результатом этого создания должен быть экземпляр [`queue.Queue`](https://python-all.ru/3.16/library/queue.html#queue.Queue).381382Если ключ `queue` отсутствует, создаётся и используется стандартная неограниченная очередь [`queue.Queue`](https://python-all.ru/3.16/library/queue.html#queue.Queue).383384Если ключ `listener` присутствует, соответствующее значение может быть одним из следующих:385386- Подкласс [`logging.handlers.QueueListener`](https://python-all.ru/3.16/library/logging.handlers.html#logging.handlers.QueueListener). Это, конечно, возможно только если вы создаёте или изменяете словарь конфигурации в коде.387- Строка, которая разрешается в класс, являющийся подклассом `QueueListener`, например, `'my.package.CustomListener'`.388- Словарь с ключом `'()'`, который создаётся обычным способом, как описано в [Пользовательские объекты](https://python-all.ru/3.16/library/logging.config.html#logging-config-dict-userdef). Результатом этого создания должен быть вызываемый объект с той же сигнатурой, что и у инициализатора `QueueListener`.389390Если ключ `listener` отсутствует, используется [`logging.handlers.QueueListener`](https://python-all.ru/3.16/library/logging.handlers.html#logging.handlers.QueueListener).391392Значения ключа `handlers` – это имена других обработчиков в конфигурации (не показаны в приведённом фрагменте), которые будут переданы слушателю очереди.393394Любые пользовательские классы обработчика очереди и слушателя должны быть определены с теми же сигнатурами инициализации, что и [`QueueHandler`](https://python-all.ru/3.16/library/logging.handlers.html#logging.handlers.QueueHandler) и [`QueueListener`](https://python-all.ru/3.16/library/logging.handlers.html#logging.handlers.QueueListener).395396Добавлено в версии 3.12.397398## Формат файла конфигурации399400Формат конфигурационного файла, который понимает [`fileConfig()`](https://python-all.ru/3.16/library/logging.config.html#logging.config.fileConfig), основан на функциональности [`configparser`](https://python-all.ru/3.16/library/configparser.html#module-configparser). Файл должен содержать разделы с названиями `[loggers]`, `[handlers]` и `[formatters]`, которые идентифицируют по имени сущности каждого типа, определенные в файле. Для каждой такой сущности существует отдельный раздел, который определяет, как эта сущность настраивается. Так, для регистратора с именем `log01` в разделе `[loggers]` соответствующие детали конфигурации хранятся в разделе `[logger_log01]`. Аналогично, обработчик с именем `hand01` в разделе `[handlers]` будет иметь свою конфигурацию в разделе с названием `[handler_hand01]`, а форматировщик с именем `form01` в разделе `[formatters]` будет иметь свою конфигурацию в разделе `[formatter_form01]`. Конфигурация корневого регистратора должна быть задана в разделе `[logger_root]`.401402> **Примечание**403>404> API [`fileConfig()`](https://python-all.ru/3.16/library/logging.config.html#logging.config.fileConfig) старше, чем API [`dictConfig()`](https://python-all.ru/3.16/library/logging.config.html#logging.config.dictConfig), и не предоставляет функциональности для покрытия некоторых аспектов логирования. Например, вы не можете настроить объекты [`Filter`](https://python-all.ru/3.16/library/logging.html#logging.Filter), которые обеспечивают фильтрацию сообщений, выходящую за рамки простых целочисленных уровней, с помощью `fileConfig()`. Если вам нужны экземпляры `Filter` в вашей конфигурации логирования, вам придется использовать `dictConfig()`. Обратите внимание, что будущие улучшения функциональности конфигурации будут добавляться в `dictConfig()`, так что стоит рассмотреть переход на этот более новый API, когда это удобно.405406Примеры таких разделов в файле приведены ниже.407408```ini409[loggers]410keys=root,log02,log03,log04,log05,log06,log07411412[handlers]413keys=hand01,hand02,hand03,hand04,hand05,hand06,hand07,hand08,hand09414415[formatters]416keys=form01,form02,form03,form04,form05,form06,form07,form08,form09417```418419Корневой регистратор должен указывать уровень и список обработчиков. Пример раздела корневого регистратора приведен ниже.420421```ini422[logger_root]423level=NOTSET424handlers=hand01425```426427Запись `level` может быть `DEBUG, INFO, WARNING, ERROR, CRITICAL` или `NOTSET`. Только для корневого регистратора `NOTSET` означает, что все сообщения будут зарегистрированы. Значения уровней [вычисляются](https://python-all.ru/3.16/library/functions.html#func-eval) в контексте пространства имен пакета `logging`.428429Запись `handlers` представляет собой список имен обработчиков, разделенных запятыми; эти имена должны присутствовать в разделе `[handlers]`. Данные имена должны присутствовать в разделе `[handlers]` и иметь соответствующие разделы в конфигурационном файле.430431Для регистраторов, отличных от корневого, требуется некоторая дополнительная информация. Это проиллюстрировано следующим примером.432433```ini434[logger_parser]435level=DEBUG436handlers=hand01437propagate=1438qualname=compiler.parser439```440441Записи `level` и `handlers` интерпретируются так же, как для корневого регистратора, за исключением того, что если уровень не корневого регистратора указан как `NOTSET`, система обращается к регистраторам выше по иерархии, чтобы определить эффективный уровень регистратора. Запись `propagate` устанавливается в 1, чтобы указать, что сообщения должны распространяться на обработчики выше по иерархии регистраторов от этого регистратора, или в 0, чтобы указать, что сообщения **не** распространяются на обработчики выше по иерархии. Запись `qualname` – это иерархическое имя канала регистратора, то есть имя, используемое приложением для получения регистратора.442443Разделы, которые задают конфигурацию обработчика, иллюстрируются следующим примером.444445```ini446[handler_hand01]447class=StreamHandler448level=NOTSET449formatter=form01450args=(sys.stdout,)451```452453Запись `class` указывает класс обработчика (как определено [`eval()`](https://python-all.ru/3.16/library/functions.html#eval) в пространстве имен пакета `logging`). Запись `level` интерпретируется так же, как для регистраторов, а `NOTSET` означает «регистрировать всё».454455Запись `formatter` указывает ключевое имя форматировщика для этого обработчика. Если пусто, используется форматировщик по умолчанию (`logging._defaultFormatter`). Если указано имя, оно должно присутствовать в разделе `[formatters]` и иметь соответствующий раздел в конфигурационном файле.456457Запись `args`, при [вычислении](https://python-all.ru/3.16/library/functions.html#func-eval) в контексте пространства имен пакета `logging`, представляет собой список аргументов для конструктора класса обработчика. Обратитесь к конструкторам соответствующих обработчиков или к примерам ниже, чтобы увидеть, как строятся типичные записи. Если не указано, по умолчанию `()`.458459Необязательная запись `kwargs`, при [вычислении](https://python-all.ru/3.16/library/functions.html#func-eval) в контексте пространства имен пакета `logging`, представляет собой словарь именованных аргументов для конструктора класса обработчика. Если не указано, по умолчанию `{}`.460461```ini462[handler_hand02]463class=FileHandler464level=DEBUG465formatter=form02466args=('python.log', 'w')467468[handler_hand03]469class=handlers.SocketHandler470level=INFO471formatter=form03472args=('localhost', handlers.DEFAULT_TCP_LOGGING_PORT)473474[handler_hand04]475class=handlers.DatagramHandler476level=WARN477formatter=form04478args=('localhost', handlers.DEFAULT_UDP_LOGGING_PORT)479480[handler_hand05]481class=handlers.SysLogHandler482level=ERROR483formatter=form05484args=(('localhost', handlers.SYSLOG_UDP_PORT), handlers.SysLogHandler.LOG_USER)485486[handler_hand06]487class=handlers.NTEventLogHandler488level=CRITICAL489formatter=form06490args=('Python Application', '', 'Application')491492[handler_hand07]493class=handlers.SMTPHandler494level=WARN495formatter=form07496args=('localhost', 'from@abc', ['user1@abc', 'user2@xyz'], 'Logger Subject')497kwargs={'timeout': 10.0}498499[handler_hand08]500class=handlers.MemoryHandler501level=NOTSET502formatter=form08503target=504args=(10, ERROR)505506[handler_hand09]507class=handlers.HTTPHandler508level=NOTSET509formatter=form09510args=('localhost:9022', '/log', 'GET')511kwargs={'secure': True}512```513514Разделы, которые задают конфигурацию форматировщика, обычно выглядят следующим образом.515516```ini517[formatter_form01]518format=F1 %(asctime)s %(levelname)s %(message)s %(customfield)s519datefmt=520style=%521validate=True522defaults={'customfield': 'defaultvalue'}523class=logging.Formatter524```525526Аргументы для конфигурации форматировщика такие же, как ключи в схеме словаря [раздела formatters](https://python-all.ru/3.16/library/logging.config.html#logging-config-dictschema-formatters).527528Запись `defaults`, при [вычислении](https://python-all.ru/3.16/library/functions.html#func-eval) в контексте пространства имен пакета `logging`, представляет собой словарь значений по умолчанию для пользовательских полей форматирования. Если не указано, по умолчанию `None`.529530> **Примечание**531>532> Из-за использования [`eval()`](https://python-all.ru/3.16/library/functions.html#eval), как описано выше, существуют потенциальные угрозы безопасности, связанные с использованием [`listen()`](https://python-all.ru/3.16/library/logging.config.html#logging.config.listen) для отправки и получения конфигураций через сокеты. Риски ограничены случаями, когда несколько пользователей, не доверяющих друг другу, запускают код на одной машине; обратитесь к документации `listen()` для получения дополнительной информации.533534> **См. также**535>536> **Модуль [`logging`](https://python-all.ru/3.16/library/logging.html#module-logging)**537>538> Справочник API для модуля logging.539>540> **Модуль [`logging.handlers`](https://python-all.ru/3.16/library/logging.handlers.html#module-logging.handlers)**541>542> Полезные обработчики, входящие в состав модуля logging.543