logging.config
— Конфигурация ведения журнала¶
Источник: Lib/logging/config.py
В этом разделе описывается API для настройки модуля протоколирования.
Функции конфигурации¶
Следующие функции настраивают модуль протоколирования. Они расположены в модуле logging.config
. Их использование необязательно — вы можете настроить модуль протоколирования с помощью этих функций или путем вызова основного API (определенного в самом logging
) и определения обработчиков, которые объявляются либо в logging
, либо в logging.handlers
.
- logging.config.dictConfig(config)¶
Берет конфигурацию протоколирования из словаря. Содержимое этого словаря описано в Схема словаря конфигурации ниже.
Если в процессе конфигурирования возникла ошибка, эта функция выдаст сообщение
ValueError
,TypeError
,AttributeError
илиImportError
с соответствующим описанием. Ниже приведен (возможно, неполный) список условий, при которых возникает ошибка:Значение
level
, которое не является строкой или является строкой, не соответствующей фактическому уровню протоколирования.Значение
propagate
, которое не является булевой функцией.Идентификатор, не имеющий соответствующего назначения.
Во время инкрементного вызова обнаружен несуществующий идентификатор обработчика.
Недопустимое имя регистратора.
Неспособность определить внутренний или внешний объект.
Парсинг выполняется классом
DictConfigurator
, конструктору которого передается словарь, используемый для конфигурации, и методconfigure()
. Модульlogging.config
имеет вызываемый атрибутdictConfigClass
, который изначально имеет значениеDictConfigurator
. Вы можете заменить значениеdictConfigClass
на подходящую собственную реализацию.dictConfig()
вызываетdictConfigClass
, передавая указанный словарь, а затем вызывает методconfigure()
на возвращаемом объекте, чтобы ввести конфигурацию в действие:def dictConfig(config): dictConfigClass(config).configure()
Например, подкласс
DictConfigurator
может вызватьDictConfigurator.__init__()
в своем собственном__init__()
, затем установить пользовательские префиксы, которые будут использоваться в последующем вызовеconfigure()
.dictConfigClass
будет привязан к этому новому подклассу, а затемdictConfig()
может быть вызван точно так же, как и в стандартном, не настроенном состоянии.Added in version 3.2.
- logging.config.fileConfig(fname, defaults=None, disable_existing_loggers=True, encoding=None)¶
Считывает конфигурацию протоколирования из файла
configparser
-формата. Формат файла должен быть таким, как описано в Формат файла конфигурации. Эта функция может быть вызвана несколько раз из приложения, позволяя конечному пользователю выбирать из различных заранее подготовленных конфигураций (если разработчик предоставляет механизм для представления вариантов и загрузки выбранной конфигурации).Он поднимет значение
FileNotFoundError
, если файл не существует, иRuntimeError
, если файл недействителен или пуст.- Параметры:
fname – Имя файла, или файлоподобный объект, или экземпляр, производный от
RawConfigParser
. Если передан экземпляр, производный отRawConfigParser
, он используется как есть. В противном случае инстанцируетсяConfigParser
, и конфигурация считывается им из объекта, переданного вfname
. Если у этого объекта есть методreadline()
, предполагается, что это файлоподобный объект, и он считывается с помощьюread_file()
; в противном случае предполагается, что это имя файла, и оно передается вread()
.defaults – В этом аргументе можно указать значения по умолчанию, которые будут передаваться в
ConfigParser
.disable_existing_loggers – Если указать
False
, то логгеры, существующие на момент вызова, останутся включенными. По умолчанию используется значениеTrue
, поскольку это позволяет использовать старое поведение, совместимое с обратным ходом событий. Это поведение заключается в отключении всех существующих не корневых логгеров, если только они или их предки не названы явно в конфигурации логгирования.encoding – Кодировка, используемая для открытия файла, когда fname - это имя файла.
Изменено в версии 3.4: Экземпляр подкласса
RawConfigParser
теперь принимается в качестве значения дляfname
. Это облегчает:Использование файла конфигурации, в котором настройка протоколирования является лишь частью общей конфигурации приложения.
Использование конфигурации, считанной из файла, а затем измененной использующим приложением (например, на основе параметров командной строки или других аспектов среды выполнения) перед передачей в
fileConfig
.
Изменено в версии 3.10: Добавлен параметр encoding.
Изменено в версии 3.12: Если предоставленный файл не существует или является недействительным или пустым, будет выброшено исключение.
- logging.config.listen(port=DEFAULT_LOGGING_CONFIG_PORT, verify=None)¶
Запускает сокет-сервер на указанном порту и прослушивает новые конфигурации. Если порт не указан, используется порт по умолчанию
DEFAULT_LOGGING_CONFIG_PORT
. Конфигурации журнала будут отправлены в виде файла, пригодного для обработкиdictConfig()
илиfileConfig()
. Возвращает экземплярThread
, на котором вы можете вызватьstart()
для запуска сервера и который вы можетеjoin()
при необходимости. Чтобы остановить сервер, вызовитеstopListening()
.Аргумент
verify
, если он указан, должен представлять собой вызываемый модуль, который должен проверить, являются ли байты, полученные через сокет, действительными и должны ли они быть обработаны. Это может быть сделано путем шифрования и/или подписи того, что отправляется через сокет, так что вызываемыйverify
может выполнить проверку подписи и/или дешифровку. Вызываемый модульverify
вызывается с единственным аргументом - байтами, полученными через сокет, и должен возвращать байты для обработки илиNone
, чтобы указать, что байты должны быть отброшены. Возвращаемые байты могут быть такими же, как и переданные (например, когда выполняется только проверка), а могут быть совершенно другими (возможно, если выполняется расшифровка).Чтобы отправить конфигурацию на сокет, прочитайте файл конфигурации и отправьте его на сокет в виде последовательности байтов, которым предшествует строка длиной четыре байта, упакованная в двоичном формате с помощью
struct.pack('>L', n)
.Примечание
Поскольку часть конфигурации передается через
eval()
, использование этой функции может подвергнуть ее пользователей риску безопасности. Хотя функция привязывается только к сокету наlocalhost
и не принимает соединения с удаленных машин, существуют сценарии, в которых недоверенный код может быть запущен под учетной записью процесса, вызывающегоlisten()
. В частности, если процесс, вызывающийlisten()
, работает на многопользовательской машине, где пользователи не могут доверять друг другу, то злоумышленник может организовать запуск по сути произвольного кода в процессе пользователя-жертвы, просто подключившись к его сокетуlisten()
и отправив конфигурацию, которая запускает любой код, который злоумышленник хочет получить в процессе жертвы. Это особенно легко сделать, если используется порт по умолчанию, но несложно, даже если используется другой порт. Чтобы избежать этого, используйте аргументverify
вlisten()
, чтобы предотвратить применение нераспознанных конфигураций.Изменено в версии 3.4: Был добавлен аргумент
verify
.Примечание
Если вы хотите отправить слушателю конфигурации, которые не отключают существующие регистраторы, вам нужно использовать формат JSON для конфигурации, в котором для конфигурации будет использоваться
dictConfig()
. Этот метод позволяет указатьdisable_existing_loggers
в качествеFalse
в отправляемой конфигурации.
Соображения безопасности¶
Функциональность конфигурации протоколирования пытается предложить удобство, и отчасти это достигается за счет возможности преобразования текста в конфигурационных файлах в объекты Python, используемые в конфигурации протоколирования - например, как описано в Объекты, определяемые пользователем. Однако эти же механизмы (импорт вызываемых объектов из пользовательских модулей и их вызов с параметрами из конфигурации) могут быть использованы для вызова любого кода, и по этой причине вам следует относиться к конфигурационным файлам из ненадежных источников с крайней осторожностью и убедиться, что ничего плохого не произойдет, если вы загрузите их, прежде чем загружать их на самом деле.
Схема словаря конфигурации¶
Описание конфигурации протоколирования требует перечисления различных объектов, которые необходимо создать, и связей между ними; например, вы можете создать обработчик с именем „console“, а затем сказать, что регистратор с именем „startup“ будет отправлять свои сообщения обработчику „console“. Эти объекты не ограничиваются теми, что предоставляются модулем logging
, поскольку вы можете написать свой собственный форматтер или класс обработчика. В параметры этих классов также могут потребоваться внешние объекты, такие как sys.stderr
. Синтаксис для описания этих объектов и связей определен в Объектные соединения ниже.
Детали схемы словаря¶
Словарь, переданный в dictConfig()
, должен содержать следующие ключи:
version - устанавливается в целочисленное значение, представляющее версию схемы. Единственное допустимое значение на данный момент - 1, но наличие этого ключа позволяет схеме развиваться, сохраняя при этом обратную совместимость.
Все остальные ключи необязательны, но если они присутствуют, то будут интерпретироваться так, как описано ниже. Во всех описанных ниже случаях, когда упоминается «конфигурирующий dict», он будет проверен на наличие специального ключа '()'
, чтобы определить, требуется ли пользовательский инстанс. Если да, то для создания экземпляра используется механизм, описанный в Объекты, определяемые пользователем ниже; в противном случае контекст используется для определения того, что нужно инстанцировать.
formatters - соответствующее значение будет представлять собой dict, в котором каждый ключ - это идентификатор форматера, а каждое значение - это dict, описывающий, как настроить соответствующий экземпляр
Formatter
.В конфигурационном dict выполняется поиск следующих необязательных ключей, которые соответствуют аргументам, переданным для создания объекта
Formatter
:format
datefmt
style
validate
(начиная с версии >=3.8)defaults
(начиная с версии >=3.12)
Необязательный ключ
class
указывает на имя класса форматировщика (в виде пунктира из имени модуля и класса). Аргументы инстанцирования такие же, как и дляFormatter
, поэтому этот ключ наиболее полезен для инстанцирования специализированного подклассаFormatter
. Например, альтернативный класс может представлять трассировку исключений в расширенном или сжатом формате. Если ваш форматтер требует других или дополнительных ключей конфигурации, вам следует использовать Объекты, определяемые пользователем.filters - соответствующее значение будет представлено диктой, в которой каждый ключ - это идентификатор фильтра, а каждое значение - это диктант, описывающий, как настроить соответствующий экземпляр фильтра.
В конфигурационной дикте ищется ключ
name
(по умолчанию пустая строка), который используется для создания экземпляраlogging.Filter
.handlers - соответствующее значение будет представлено диктой, в которой каждый ключ - это идентификатор обработчика, а каждое значение - диктант, описывающий, как настроить соответствующий экземпляр обработчика.
В дикте конфигурации выполняется поиск следующих ключей:
class
(обязательно). Это полное имя класса обработчика.level
(необязательно). Уровень обработчика.formatter
(необязательно). Идентификатор форматера для этого обработчика.filters
(необязательно). Список идентификаторов фильтров для этого обработчика.Изменено в версии 3.11:
filters
может принимать экземпляры фильтров в дополнение к идентификаторам.
Все другие ключи передаются в конструктор обработчика в качестве аргументов ключевых слов. Например, в данном фрагменте:
handlers: console: class : logging.StreamHandler formatter: brief level : INFO filters: [allow_foo] stream : ext://sys.stdout file: class : logging.handlers.RotatingFileHandler formatter: precise filename: logconfig.log maxBytes: 1024 backupCount: 3
обработчик с идентификатором
console
инстанцируется какlogging.StreamHandler
, используяsys.stdout
в качестве базового потока. Обработчик с идентификаторомfile
инстанцируется какlogging.handlers.RotatingFileHandler
с аргументами ключевого словаfilename='logconfig.log', maxBytes=1024, backupCount=3
.loggers - соответствующее значение будет представлять собой dict, в котором каждый ключ - это имя логгера, а каждое значение - dict, описывающий, как настроить соответствующий экземпляр логгера.
В дикте конфигурации выполняется поиск следующих ключей:
level
(необязательно). Уровень регистратора.propagate
(необязательно). Настройка распространения для регистратора.filters
(необязательно). Список идентификаторов фильтров для этого регистратора.Изменено в версии 3.11:
filters
может принимать экземпляры фильтров в дополнение к идентификаторам.handlers
(необязательно). Список идентификаторов обработчиков для этого регистратора.
Указанные регистраторы будут настроены в соответствии с указанными уровнем, распространением, фильтрами и обработчиками.
root - это будет конфигурация для корневого регистратора. Обработка конфигурации будет такой же, как и для любого регистратора, за исключением того, что параметр
propagate
не будет применяться.incremental - интерпретировать ли конфигурацию как инкрементную по отношению к существующей конфигурации. По умолчанию это значение равно
False
, что означает, что указанная конфигурация заменяет существующую конфигурацию с той же семантикой, которая используется в существующемfileConfig()
. API.Если указанное значение равно
True
, конфигурация обрабатывается, как описано в разделе Инкрементная конфигурация.disable_existing_loggers - нужно ли отключать все существующие некорневые регистраторы. Этот параметр отражает одноименный параметр в
fileConfig()
. Если параметр отсутствует, то по умолчанию он принимает значениеTrue
. Это значение игнорируется, если параметр incremental имеет значениеTrue
.
Инкрементная конфигурация¶
Трудно обеспечить полную гибкость для инкрементной конфигурации. Например, поскольку такие объекты, как фильтры и форматеры, являются анонимными, после создания конфигурации невозможно ссылаться на такие анонимные объекты при дополнении конфигурации.
Кроме того, не существует убедительных аргументов в пользу произвольного изменения объектного графа регистраторов, обработчиков, фильтров и форматоров во время выполнения, когда конфигурация уже настроена; многословность регистраторов и обработчиков можно контролировать, просто задавая уровни (и, в случае регистраторов, флаги распространения). Произвольное изменение графа объектов безопасным способом проблематично в многопоточной среде; хотя это и не невозможно, преимущества не стоят той сложности, которую добавляет реализация.
Таким образом, если ключ incremental
в конфигурационной дикте присутствует и равен True
, система будет полностью игнорировать любые записи formatters
и filters
и обрабатывать только настройки level
в записях handlers
и настройки level
и propagate
в записях loggers
и root
.
Использование значения в дикте конфигурации позволяет передавать конфигурацию по проводам в виде маринованных диктов слушателю сокета. Таким образом, можно со временем изменять степень подробности протоколирования в долго работающем приложении без необходимости останавливать и перезапускать приложение.
Объектные соединения¶
Схема описывает набор объектов протоколирования - логгеров, обработчиков, форматоров, фильтров, - которые связаны друг с другом в графе объектов. Таким образом, схема должна представлять связи между объектами. Например, допустим, что после настройки определенный логгер привязан к определенному обработчику. Для целей данного обсуждения можно сказать, что регистратор представляет собой источник, а обработчик - пункт назначения связи между ними. Конечно, в конфигурируемых объектах это представлено регистратором, хранящим ссылку на обработчик. В дикте конфигурации это делается путем присвоения каждому объекту назначения идентификатора, который однозначно его идентифицирует, а затем использования этого идентификатора в конфигурации объекта-источника для указания на то, что между объектом-источником и объектом назначения с этим идентификатором существует соединение.
Например, рассмотрим следующий фрагмент YAML:
formatters:
brief:
# configuration for formatter with id 'brief' goes here
precise:
# configuration for formatter with id 'precise' goes here
handlers:
h1: #This is an id
# configuration of handler with id 'h1' goes here
formatter: brief
h2: #This is another id
# configuration of handler with id 'h2' goes here
formatter: precise
loggers:
foo.bar.baz:
# other configuration for logger 'foo.bar.baz'
handlers: [h1, h2]
(Примечание: здесь используется YAML, потому что он немного более читабелен, чем эквивалентная исходная форма словаря на Python).
Идентификаторы для регистраторов - это имена регистраторов, которые будут использоваться программно для получения ссылки на эти регистраторы, например foo.bar.baz
. Идентификаторы для форматоров и фильтров могут быть любыми строковыми значениями (например, brief
, precise
выше), и они являются переходными, так как имеют значение только для обработки словаря конфигурации и используются для определения связей между объектами, и не сохраняются нигде после завершения вызова конфигурации.
Приведенный выше фрагмент указывает, что к логгеру с именем foo.bar.baz
должны быть прикреплены два обработчика, которые описываются идентификаторами обработчиков h1
и h2
. Форматировщик для h1
описывается идентификатором brief
, а форматор для h2
- идентификатором precise
.
Объекты, определяемые пользователем¶
Схема поддерживает пользовательские объекты для обработчиков, фильтров и форматоров. (Логгерам не нужно иметь разные типы для разных экземпляров, поэтому в этой схеме конфигурации нет поддержки пользовательских классов логгеров).
Конфигурируемые объекты описываются словарями, в которых подробно описывается их конфигурация. В некоторых местах система протоколирования сможет определить из контекста, как должен быть инстанцирован объект, но когда нужно инстанцировать объект, определяемый пользователем, система не будет знать, как это сделать. Чтобы обеспечить полную гибкость при инстанцировании пользовательских объектов, пользователь должен предоставить «фабрику» - вызываемый объект, который вызывается со словарем конфигурации и возвращает инстанцированный объект. Об этом сигнализирует абсолютный путь импорта к фабрике, доступный под специальным ключом '()'
. Вот конкретный пример:
formatters:
brief:
format: '%(message)s'
default:
format: '%(asctime)s %(levelname)-8s %(name)-15s %(message)s'
datefmt: '%Y-%m-%d %H:%M:%S'
custom:
(): my.package.customFormatterFactory
bar: baz
spam: 99.9
answer: 42
В приведенном выше фрагменте YAML определены три форматера. Первый, с идентификатором brief
, представляет собой стандартный экземпляр logging.Formatter
с указанной строкой формата. Второй, с идентификатором default
, имеет более длинный формат, а также явно определяет формат времени, и в результате будет создан logging.Formatter
, инициализированный этими двумя строками формата. Показанные в исходном тексте Python, форматеры brief
и default
имеют конфигурационные подсловари:
{
'format' : '%(message)s'
}
и:
{
'format' : '%(asctime)s %(levelname)-8s %(name)-15s %(message)s',
'datefmt' : '%Y-%m-%d %H:%M:%S'
}
соответственно, и поскольку эти словари не содержат специального ключа '()'
, инстанцирование определяется из контекста: в результате создаются стандартные экземпляры logging.Formatter
. Конфигурационный подсловарь для третьего форматера с идентификатором custom
имеет следующий вид:
{
'()' : 'my.package.customFormatterFactory',
'bar' : 'baz',
'spam' : 99.9,
'answer' : 42
}
и содержит специальный ключ '()'
, что означает, что требуется пользовательское инстанцирование. В этом случае будет использован указанный фабричный вызываемый модуль. Если это фактический вызываемый элемент, то он будет использован напрямую - в противном случае, если вы укажете строку (как в примере), фактический вызываемый элемент будет найден с помощью обычных механизмов импорта. Вызываемый модуль будет вызван с оставшимися элементами в конфигурационном подсловаре в качестве аргументов ключевых слов. В приведенном выше примере предполагается, что вызов вернет форматер с идентификатором custom
:
my.package.customFormatterFactory(bar='baz', spam=99.9, answer=42)
Предупреждение
Значения ключей bar
, spam
и answer
в приведенном выше примере не должны быть словарями конфигурации или ссылками, такими как cfg://foo
или ext://bar
, поскольку они не будут обработаны механизмом конфигурации, а будут переданы в вызываемый модуль как есть.
Ключ '()'
был использован в качестве специального ключа, поскольку он не является действительным именем параметра ключевого слова и поэтому не будет конфликтовать с именами аргументов ключевого слова, используемых в вызове. Ключ '()'
также служит мнемоникой, указывающей на то, что соответствующее значение является вызываемым.
Изменено в версии 3.11: Член filters
в handlers
и loggers
может принимать не только идентификаторы, но и экземпляры фильтров.
Вы также можете указать специальный ключ '.'
, значением которого является словарь, представляющий собой отображение имен атрибутов на значения. Если указанные атрибуты найдены, они будут установлены в пользовательском объекте до его возврата. Таким образом, при следующей конфигурации:
{
'()' : 'my.package.customFormatterFactory',
'bar' : 'baz',
'spam' : 99.9,
'answer' : 42,
'.' {
'foo': 'bar',
'baz': 'bozz'
}
}
возвращаемый форматтер будет иметь атрибут foo
, установленный в 'bar'
, и атрибут baz
, установленный в 'bozz'
.
Предупреждение
Значения атрибутов foo
и baz
в приведенном выше примере не должны быть словарями конфигурации или ссылками, такими как cfg://foo
или ext://bar
, потому что они не будут обрабатываться механизмом конфигурации, а будут установлены как значения атрибутов as-is.
Порядок настройки обработчика¶
Обработчики конфигурируются в алфавитном порядке их ключей, и сконфигурированный обработчик заменяет словарь конфигурации в (рабочей копии) словаря handlers
в схеме. Если вы используете такую конструкцию, как cfg://handlers.foo
, то первоначально handlers['foo']
указывает на словарь конфигурации для обработчика с именем foo
, а затем (после того как этот обработчик будет настроен) - на настроенный экземпляр обработчика. Таким образом, cfg://handlers.foo
может разрешаться либо в словарь, либо в экземпляр обработчика. Вообще, разумно называть обработчики таким образом, чтобы зависимые обработчики конфигурировались _после_ всех обработчиков, от которых они зависят; это позволяет использовать что-то вроде cfg://handlers.foo
при конфигурировании обработчика, который зависит от обработчика foo
. Если бы этот зависимый обработчик был назван bar
, возникли бы проблемы, поскольку конфигурирование bar
было бы предпринято до конфигурирования foo
, а foo
еще не был бы сконфигурирован. Однако если бы зависимый обработчик был назван foobar
, он был бы сконфигурирован после foo
, в результате чего cfg://handlers.foo
разрешился бы в сконфигурированный обработчик foo
, а не в его словарь конфигурации.
Доступ к внешним объектам¶
Бывают случаи, когда конфигурация должна ссылаться на внешние по отношению к ней объекты, например sys.stderr
. Если диктант конфигурации строится с помощью кода на Python, это несложно, но возникает проблема, когда конфигурация предоставляется через текстовый файл (например, JSON, YAML). В текстовом файле нет стандартного способа отличить sys.stderr
от буквенной строки 'sys.stderr'
. Чтобы облегчить это различие, система конфигурации ищет определенные специальные префиксы в строковых значениях и обрабатывает их особым образом. Например, если в качестве значения в конфигурации указана литеральная строка 'ext://sys.stderr'
, то префикс ext://
будет удален, а оставшаяся часть значения будет обработана с помощью обычных механизмов импорта.
Работа с такими префиксами аналогична работе с протоколами: существует общий механизм поиска префиксов, соответствующих регулярному выражению ^(?P<prefix>[a-z]+)://(?P<suffix>.*)$
, и если prefix
распознан, то suffix
обрабатывается в зависимости от префикса, и результат обработки заменяет строковое значение. Если префикс не распознан, то строковое значение будет оставлено как есть.
Доступ к внутренним объектам¶
Помимо внешних объектов, иногда возникает необходимость ссылаться на объекты в конфигурации. Это будет сделано неявно системой конфигурации для вещей, о которых она знает. Например, строковое значение 'DEBUG'
для level
в регистраторе или обработчике будет автоматически преобразовано в значение logging.DEBUG
, а записи handlers
, filters
и formatter
будут принимать идентификатор объекта и преобразовываться в соответствующий целевой объект.
Однако для пользовательских объектов, которые не известны модулю logging
, необходим более общий механизм. Например, рассмотрим logging.handlers.MemoryHandler
, который принимает аргумент target
, являющийся другим обработчиком, на который можно делегировать полномочия. Поскольку система уже знает об этом классе, то в конфигурации заданный target
должен быть просто идентификатором объекта соответствующего целевого обработчика, и система разрешит обработчик по этому идентификатору. Однако если пользователь определит my.package.MyHandler
, у которого есть обработчик alternate
, система конфигурации не будет знать, что alternate
ссылается на обработчик. Чтобы учесть это, система общего разрешения позволяет пользователю указать:
handlers:
file:
# configuration of file handler goes here
custom:
(): my.package.MyHandler
alternate: cfg://handlers.file
Буквальная строка 'cfg://handlers.file'
будет разрешена аналогично строкам с префиксом ext://
, но искать ее нужно в самой конфигурации, а не в пространстве имен импорта. Механизм позволяет получить доступ по точке или по индексу, аналогично тому, как это сделано в str.format
. Таким образом, если взять следующий фрагмент:
handlers:
email:
class: logging.handlers.SMTPHandler
mailhost: localhost
fromaddr: my_app@domain.tld
toaddrs:
- support_team@domain.tld
- dev_team@domain.tld
subject: Houston, we have a problem.
в конфигурации строка '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]'
. Последняя форма используется только в том случае, если ключ содержит пробелы или неалфавитно-цифровые символы. Обратите внимание, что символы [
и ]
в ключах недопустимы. Если значение индекса состоит только из десятичных цифр, доступ будет осуществляться по соответствующему целочисленному значению, при необходимости возвращаясь к строковому значению.
Если задана строка 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']
, если это не удастся.
Разрешение импорта и пользовательские импортеры¶
Разрешение импорта по умолчанию использует встроенную функцию __import__()
для выполнения импорта. Возможно, вы захотите заменить ее собственным механизмом импорта: в этом случае вы можете заменить атрибут importer
в классе DictConfigurator
или его суперклассе, классе BaseConfigurator
. Однако следует быть осторожным из-за способа доступа к функциям из классов через дескрипторы. Если вы используете вызываемую функцию Python для импорта и хотите определить ее на уровне класса, а не экземпляра, вам нужно обернуть ее с помощью staticmethod()
. Например:
from importlib import import_module
from logging.config import BaseConfigurator
BaseConfigurator.importer = staticmethod(import_module)
Вам не нужно обертывать staticmethod()
, если вы устанавливаете вызов импорта на конфигураторе экземпляре.
Настройка QueueHandler и QueueListener¶
Если вы хотите сконфигурировать QueueHandler
, то, учитывая, что он обычно используется вместе с QueueListener
, вы можете сконфигурировать оба вместе. После настройки экземпляр QueueListener
будет доступен в качестве атрибута listener
созданного обработчика, а тот, в свою очередь, будет доступен вам, используя getHandlerByName()
и передавая имя, которое вы использовали для QueueHandler
в своей конфигурации. Схема словаря для настройки пары показана в примере YAML-фрагмента ниже.
handlers:
qhand:
class: logging.handlers.QueueHandler
queue: my.module.queue_factory
listener: my.package.CustomListener
handlers:
- hand_name_1
- hand_name_2
...
Клавиши queue
и listener
являются необязательными.
Если присутствует ключ queue
, соответствующее значение может быть одним из следующих:
Действительный экземпляр
queue.Queue
или его подкласс. Это, конечно, возможно только в том случае, если вы создаете или изменяете словарь конфигурации в коде.Строка, которая разрешается в вызываемую функцию, которая при вызове без аргументов возвращает экземпляр
queue.Queue
, который необходимо использовать. Этот вызываемый элемент может быть подклассомqueue.Queue
или функцией, возвращающей подходящий экземпляр очереди, напримерmy.module.queue_factory()
.Дикта с ключом
'()'
, которая строится обычным способом, как описано в Объекты, определяемые пользователем. Результатом этого построения должен быть экземплярqueue.Queue
.
Если ключ queue
отсутствует, создается и используется стандартный неограниченный экземпляр queue.Queue
.
Если присутствует ключ listener
, соответствующее значение может быть одним из следующих:
Подкласс
logging.handlers.QueueListener
. Это, конечно, возможно только в том случае, если вы создаете или изменяете словарь конфигурации в коде.Строка, которая разрешается в класс, являющийся подклассом
QueueListener
, например'my.package.CustomListener'
.Дикта с ключом
'()'
, который строится обычным способом, как описано в Объекты, определяемые пользователем. Результатом этой конструкции должен быть вызываемый объект с той же сигнатурой, что и инициализаторQueueListener
.
Если клавиша listener
отсутствует, используется клавиша logging.handlers.QueueListener
.
Значения под ключом handlers
- это имена других обработчиков в конфигурации (не показаны в приведенном выше фрагменте), которые будут переданы слушателю очереди.
Любые пользовательские классы обработчиков и слушателей очередей должны быть определены с теми же сигнатурами инициализации, что и QueueHandler
и QueueListener
.
Added in version 3.12.
Формат файла конфигурации¶
Формат конфигурационного файла, понимаемый fileConfig()
, основан на функциональности configparser
. Файл должен содержать секции [loggers]
, [handlers]
и [formatters]
, которые идентифицируют по имени сущности каждого типа, определенные в файле. Для каждой такой сущности есть отдельная секция, которая определяет, как эта сущность конфигурируется. Так, для регистратора с именем log01
в секции [loggers]
соответствующие сведения о конфигурации содержатся в секции [logger_log01]
. Аналогично, обработчик с именем hand01
в секции [handlers]
будет иметь свою конфигурацию в секции [handler_hand01]
, а форматтер с именем form01
в секции [formatters]
будет иметь свою конфигурацию, указанную в секции [formatter_form01]
. Конфигурация корневого регистратора должна быть указана в секции [logger_root]
.
Примечание
API fileConfig()
API является более старым, чем dictConfig()
. API и не обеспечивает функциональности, покрывающей некоторые аспекты протоколирования. Например, с помощью fileConfig()
нельзя настроить объекты Filter
, которые обеспечивают фильтрацию сообщений за пределами простых целочисленных уровней. Если вам необходимо иметь экземпляры Filter
в вашей конфигурации протоколирования, вам придется использовать dictConfig()
. Обратите внимание, что в будущем функциональность конфигурации будет расширена за счет dictConfig()
, поэтому стоит рассмотреть возможность перехода на этот новый API, когда это будет удобно.
Примеры этих секций в файле приведены ниже.
[loggers]
keys=root,log02,log03,log04,log05,log06,log07
[handlers]
keys=hand01,hand02,hand03,hand04,hand05,hand06,hand07,hand08,hand09
[formatters]
keys=form01,form02,form03,form04,form05,form06,form07,form08,form09
Корневой регистратор должен указывать уровень и список обработчиков. Пример раздела корневого регистратора приведен ниже.
[logger_root]
level=NOTSET
handlers=hand01
Запись level
может быть одной из DEBUG, INFO, WARNING, ERROR, CRITICAL
или NOTSET
. Только для корневого регистратора значение NOTSET
означает, что в журнал будут записываться все сообщения. Значения уровня - это evaluated в контексте пространства имен пакета logging
.
Элемент handlers
представляет собой список имен обработчиков, разделенных запятыми, которые должны находиться в секции [handlers]
. Эти имена должны присутствовать в секции [handlers]
и иметь соответствующие разделы в конфигурационном файле.
Для регистраторов, отличных от корневого регистратора, требуется некоторая дополнительная информация. Это показано на следующем примере.
[logger_parser]
level=DEBUG
handlers=hand01
propagate=1
qualname=compiler.parser
Записи level
и handlers
интерпретируются как для корневого регистратора, за исключением того, что если уровень не корневого регистратора указан как NOTSET
, система обращается к регистраторам выше по иерархии, чтобы определить эффективный уровень регистратора. Запись propagate
имеет значение 1, чтобы указать, что сообщения должны распространяться на обработчики выше по иерархии от этого регистратора, или 0, чтобы указать, что сообщения не распространяются на обработчики выше по иерархии. Элемент qualname
- это иерархическое имя канала регистратора, то есть имя, используемое приложением для получения регистратора.
Разделы, определяющие конфигурацию обработчика, приведены ниже.
[handler_hand01]
class=StreamHandler
level=NOTSET
formatter=form01
args=(sys.stdout,)
Запись class
указывает на класс обработчика (определяется eval()
в пространстве имен пакета logging
). Запись level
интерпретируется как для регистраторов, а NOTSET
означает «регистрировать все».
Элемент formatter
указывает на ключевое имя форматера для этого обработчика. Если он пуст, используется формат по умолчанию (logging._defaultFormatter
). Если указано имя, оно должно присутствовать в секции [formatters]
и иметь соответствующий раздел в конфигурационном файле.
Запись args
, когда evaluated в контексте пространства имен пакета logging
, представляет собой список аргументов конструктора класса-обработчика. Обратитесь к конструкторам соответствующих обработчиков или к примерам ниже, чтобы увидеть, как строятся типичные записи. Если значение не указано, по умолчанию оно принимает значение ()
.
Необязательный элемент kwargs
, когда evaluated в контексте пространства имен пакета logging
, является аргументом ключевого слова dict в конструкторе класса обработчика. Если он не указан, то по умолчанию принимает значение {}
.
[handler_hand02]
class=FileHandler
level=DEBUG
formatter=form02
args=('python.log', 'w')
[handler_hand03]
class=handlers.SocketHandler
level=INFO
formatter=form03
args=('localhost', handlers.DEFAULT_TCP_LOGGING_PORT)
[handler_hand04]
class=handlers.DatagramHandler
level=WARN
formatter=form04
args=('localhost', handlers.DEFAULT_UDP_LOGGING_PORT)
[handler_hand05]
class=handlers.SysLogHandler
level=ERROR
formatter=form05
args=(('localhost', handlers.SYSLOG_UDP_PORT), handlers.SysLogHandler.LOG_USER)
[handler_hand06]
class=handlers.NTEventLogHandler
level=CRITICAL
formatter=form06
args=('Python Application', '', 'Application')
[handler_hand07]
class=handlers.SMTPHandler
level=WARN
formatter=form07
args=('localhost', 'from@abc', ['user1@abc', 'user2@xyz'], 'Logger Subject')
kwargs={'timeout': 10.0}
[handler_hand08]
class=handlers.MemoryHandler
level=NOTSET
formatter=form08
target=
args=(10, ERROR)
[handler_hand09]
class=handlers.HTTPHandler
level=NOTSET
formatter=form09
args=('localhost:9022', '/log', 'GET')
kwargs={'secure': True}
Разделы, определяющие конфигурацию форматера, типичны.
[formatter_form01]
format=F1 %(asctime)s %(levelname)s %(message)s %(customfield)s
datefmt=
style=%
validate=True
defaults={'customfield': 'defaultvalue'}
class=logging.Formatter
Аргументы для конфигурации форматера совпадают с ключами в схеме словаря formatters section.
Запись defaults
, если она evaluated в контексте пространства имен пакета logging
, представляет собой словарь значений по умолчанию для полей пользовательского форматирования. Если он не указан, по умолчанию используется значение None
.
Примечание
Из-за использования eval()
, как описано выше, существуют потенциальные риски безопасности, возникающие при использовании listen()
для отправки и получения конфигураций через сокеты. Риски ограничены случаями, когда несколько пользователей, не имеющих взаимного доверия, выполняют код на одной машине; более подробную информацию см. в документации по listen()
.
См.также
- Модуль
logging
Ссылка на API для модуля протоколирования.
- Модуль
logging.handlers
Полезные обработчики, входящие в состав модуля протоколирования.