Содержание страницы
logging.config – Конфигурация логирования¶logging.config – Logging configuration
Исходный код: Lib/logging/config.py
В этом разделе описывается API для настройки модуля logging.
Функции настройки¶Configuration functions
Следующие функции настраивают модуль logging. Они находятся в модуле logging.config. Их использование не обязательно – модуль logging можно настраивать с помощью этих функций или напрямую обращаясь к основному 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()можно будет вызывать так же, как и в состоянии по умолчанию, без настройки.Новое в версии 3.2.
-
logging.config.fileConfig(fname, defaults=None, disable_existing_loggers=True)¶ Читает конфигурацию логирования из файла в формате
configparser. Формат файла должен соответствовать описанию в разделе Формат конфигурационного файла. Эту функцию можно вызывать несколько раз в приложении, что позволяет конечному пользователю выбирать из нескольких готовых конфигураций (если разработчик предоставляет механизм для выбора и загрузки выбранной конфигурации).- Параметры
fname – имя файла, или файлоподобный объект, или экземпляр, производный от
RawConfigParser. Если передан экземпляр, производный отRawConfigParser, он используется как есть. В противном случае создаётсяConfigparser, и конфигурация считывается им из объекта, переданного вfname. Если у этого объекта есть методreadline(), предполагается, что это файлоподобный объект, и чтение выполняется черезread_file(); в противном случае считается, что это имя файла, и оно передаётся вread().defaults – значения по умолчанию, передаваемые ConfigParser, можно указать в этом аргументе.
disable_existing_loggers – если указано
False, логгеры, существующие на момент вызова, остаются включёнными. Значение по умолчанию –True, поскольку это обеспечивает обратную совместимость со старым поведением. Это поведение отключает все существующие не корневые логгеры, если только они или их предки явно не указаны в конфигурации логирования.
Изменено в версии 3.4: Экземпляр подкласса
RawConfigParserтеперь принимается как значение дляfname. Это позволяет:Использование конфигурационного файла, где настройка логирования является лишь частью общей конфигурации приложения.
Использование конфигурации, прочитанной из файла и затем изменённой приложением (например, на основе параметров командной строки или других аспектов среды выполнения) перед передачей в
fileConfig.
-
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в отправляемой конфигурации.
Соображения безопасности¶Security considerations
Функциональность конфигурации логирования старается обеспечить удобство, и отчасти это достигается за счёт возможности преобразовывать текст из файлов конфигурации в объекты Python, используемые в настройке логирования – например, как описано в разделе Пользовательские объекты. Однако те же самые механизмы (импорт вызываемых объектов из пользовательских модулей и их вызов с параметрами из конфигурации) могут быть использованы для вызова любого кода, и поэтому следует относиться к файлам конфигурации из ненадёжных источников с крайней осторожностью и убедиться, что ничего плохого не произойдёт при их загрузке, перед фактической загрузкой.
Схема словаря конфигурации¶Configuration dictionary schema
Описание конфигурации логирования требует перечисления различных создаваемых объектов и связей между ними; например, можно создать обработчик с именем «console», а затем указать, что регистратор с именем «startup» будет отправлять свои сообщения обработчику «console». Эти объекты не ограничиваются теми, что предоставляются модулем logging, поскольку можно написать собственный класс форматтера или обработчика. Параметры этих классов также могут включать внешние объекты, такие как sys.stderr. Синтаксис для описания этих объектов и связей определён ниже в разделе Связи объектов.
Подробности схемы словаря¶Dictionary Schema Details
Словарь, передаваемый dictConfig(), должен содержать следующие ключи:
version – должно быть установлено целочисленное значение, представляющее версию схемы. Единственное допустимое значение на данный момент – 1, но наличие этого ключа позволяет схеме развиваться, сохраняя обратную совместимость.
Все остальные ключи необязательны, но если они присутствуют, то будут интерпретироваться, как описано ниже. Во всех случаях, когда ниже упоминается «настраивающий словарь», в нём будет проверяться наличие специального ключа '()', чтобы определить, требуется ли пользовательское создание экземпляра. Если да, то используется механизм, описанный ниже в разделе Пользовательские объекты, для создания экземпляра; в противном случае контекст используется для определения того, что создавать.
formatters – соответствующее значение будет словарём, в котором каждый ключ – это идентификатор форматтера, а каждое значение – словарь, описывающий, как настроить соответствующий экземпляр
Formatter.В словаре конфигурации выполняется поиск ключей
formatиdatefmt(со значениями по умолчаниюNone), и они используются для создания экземпляраFormatter.Изменено в версии 3.8: ключ
validate(со значением по умолчаниюTrue) может быть добавлен в разделformattersсловаря конфигурации; это необходимо для проверки формата.filters – соответствующее значение будет словарём, в котором каждый ключ – это идентификатор фильтра, а каждое значение – словарь, описывающий, как настроить соответствующий экземпляр Filter.
В настраивающем словаре ищется ключ
name(по умолчанию пустая строка), и он используется для создания экземпляраlogging.Filter.handlers – соответствующее значение будет словарём, в котором каждый ключ – это идентификатор обработчика, а каждое значение – словарь, описывающий, как настроить соответствующий экземпляр Handler.
В словаре конфигурации ищутся следующие ключи:
class(обязательный). Это полное имя класса обработчика.level(необязательный). Уровень обработчика.formatter(необязательный). Идентификатор форматтера для этого обработчика.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 – соответствующее значение будет словарём, в котором каждый ключ – это имя регистратора, а каждое значение – словарь, описывающий, как настроить соответствующий экземпляр Logger.
В словаре конфигурации ищутся следующие ключи:
level(необязательно). Уровень регистратора.propagate(необязательно). Настройка распространения записей регистратора.filters(необязательно). Список идентификаторов фильтров для этого регистратора.handlers(необязательно). Список идентификаторов обработчиков для этого регистратора.
Указанные регистраторы будут настроены в соответствии с заданными уровнем, распространением, фильтрами и обработчиками.
root – это конфигурация корневого регистратора. Обработка конфигурации будет такой же, как и для любого другого регистратора, за исключением того, что параметр
propagateне применяется.incremental – следует ли интерпретировать конфигурацию как дополнение к существующей. Значение по умолчанию –
False, что означает, что указанная конфигурация заменяет существующую с той же семантикой, которая используется существующим APIfileConfig().Если указано значение
True, конфигурация обрабатывается так, как описано в разделе Incremental Configuration.disable_existing_loggers – следует ли отключать все существующие некорневые регистраторы. Этот параметр повторяет одноимённый параметр в
fileConfig(). Если отсутствует, по умолчанию принимает значениеTrue. Это значение игнорируется, если incremental равноTrue.
Инкрементальная конфигурация¶Incremental Configuration
Обеспечить полную гибкость для инкрементальной конфигурации сложно. Например, поскольку такие объекты, как фильтры и форматировщики, анонимны, после настройки невозможно сослаться на эти анонимные объекты при дополнении конфигурации.
Кроме того, нет убедительных причин произвольно изменять граф объектов регистраторов, обработчиков, фильтров и форматировщиков во время выполнения после настройки: уровень подробности регистрации можно контролировать просто установкой уровней (а для регистраторов – флагов распространения). Произвольное изменение графа объектов безопасным способом проблематично в многопоточной среде; хотя это возможно, выгода не стоит сложности, которую это вносит в реализацию.
Поэтому, когда ключ incremental словаря конфигурации присутствует
и равен True, система полностью игнорирует все записи formatters и
filters и обрабатывает только настройки level
в записях handlers, а также настройки level и
propagate в записях loggers и root.
Использование значения в словаре конфигурации позволяет отправлять конфигурации по сети в виде сериализованных словарей в сокетный слушатель. Таким образом, уровень подробности регистрации долго работающего приложения можно изменять со временем без необходимости останавливать и перезапускать приложение.
Соединения объектов¶Object connections
Схема описывает набор объектов логирования – регистраторы, обработчики, форматировщики, фильтры – которые соединены друг с другом в граф объектов. Таким образом, схема должна представлять соединения между объектами. Например, предположим, что после настройки к определённому регистратору прикреплён определённый обработчик. В рамках данного обсуждения можно сказать, что регистратор представляет источник, а обработчик – приёмник соединения между ними. Конечно, в настроенных объектах это реализовано тем, что регистратор хранит ссылку на обработчик. В словаре конфигурации это делается путём присвоения каждому объекту- приёмнику идентификатора, однозначно его определяющего, а затем использования этого идентификатора в конфигурации объекта-источника для указания наличия соединения между источником и объектом-приёмником с этим идентификатором.
Например, рассмотрим следующий фрагмент YAML:
formatters:
brief:
# здесь задаётся конфигурация форматтера с id 'brief'
precise:
# здесь задаётся конфигурация форматтера с id 'precise'
handlers:
h1: #Это идентификатор
# здесь задаётся конфигурация обработчика с id 'h1'
formatter: brief
h2: #Это другой идентификатор
# здесь задаётся конфигурация обработчика с id 'h2'
formatter: precise
loggers:
foo.bar.baz:
# прочая конфигурация для логгера 'foo.bar.baz'
handlers: [h1, h2]
(Примечание: YAML используется здесь, потому что он немного более читаем, чем эквивалентная форма исходного кода Python для словаря.)
Идентификаторами для регистраторов являются имена регистраторов, которые
использовались бы программно для получения ссылки на эти регистраторы, например,
foo.bar.baz. Идентификаторы для форматировщиков и фильтров могут быть любыми
строковыми значениями (такими как brief, precise выше) и являются
временными – они имеют смысл только для обработки словаря конфигурации и
используются для определения связей между объектами, и нигде не сохраняются после
завершения вызова конфигурации.
Указанный выше фрагмент показывает, что к регистратору с именем foo.bar.baz
должны быть прикреплены два обработчика, которые описываются идентификаторами
обработчиков h1 и h2. Форматировщик для h1 – это тот,
который описывается идентификатором brief, а форматировщик для h2 – тот,
который описывается идентификатором precise.
Пользовательские объекты¶User-defined objects
Схема поддерживает пользовательские объекты для обработчиков, фильтров и форматировщиков. (Регистраторам не нужны разные типы для разных экземпляров, поэтому в этой схеме конфигурации нет поддержки пользовательских классов регистраторов.)
Настраиваемые объекты описываются словарями, которые детализируют их
конфигурацию. В некоторых местах система логирования сможет определить из контекста,
как создать экземпляр объекта, но когда требуется создать экземпляр пользовательского
объекта, система не будет знать, как это сделать. Для обеспечения полной гибкости
при создании пользовательских объектов пользователь должен предоставить «фабрику» –
вызываемый объект, который вызывается со словарём конфигурации и возвращает созданный
экземпляр. Это обозначается абсолютным путём импорта к фабрике, доступным под
специальным ключом '()'. Вот конкретный пример:
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)
Ключ '()' был выбран в качестве специального, потому что он не является
допустимым именем параметра ключевого слова и, следовательно, не будет конфликтовать
с именами именованных аргументов, используемых при вызове. '()' также служит
мнемоническим напоминанием о том, что соответствующее значение является вызываемым
объектом.
Доступ к внешним объектам¶Access to external objects
Бывают случаи, когда конфигурации требуется ссылаться на объекты
внешние по отношению к конфигурации, например, sys.stderr. Если
словарь конфигурации создаётся с помощью кода Python, это
несложно, но проблема возникает, когда конфигурация задаётся
через текстовый файл (например, JSON, YAML). В текстовом файле
нет стандартного способа отличить sys.stderr от литеральной строки
'sys.stderr'. Чтобы облегчить это различие, система конфигурации
ищет определённые специальные префиксы в строковых значениях и
обрабатывает их особым образом. Например, если литеральная строка
'ext://sys.stderr' указана как значение в конфигурации,
то префикс ext:// будет отброшен, а оставшаяся часть
значения обработана с помощью стандартных механизмов импорта.
Обработка таких префиксов выполняется аналогично обработке протоколов:
существует общий механизм поиска префиксов, соответствующих регулярному выражению
^(?P<prefix>[a-z]+)://(?P<suffix>.*)$,
при котором, если префикс prefix распознан, часть suffix обрабатывается
зависящим от префикса образом, и результат обработки заменяет
строковое значение. Если префикс не распознан, строковое значение
остаётся без изменений.
Доступ к внутренним объектам¶Access to internal objects
Помимо внешних объектов, иногда также возникает необходимость ссылаться
на объекты в самой конфигурации. Это будет делаться неявно системой конфигурации
для тех вещей, о которых она знает. Например, строковое значение
'DEBUG' для level в логгере или обработчике будет
автоматически преобразовано в значение logging.DEBUG, а
записи handlers, filters и formatter будут принимать
идентификатор объекта и разрешаться в соответствующий целевой объект.
Однако необходим более общий механизм для пользовательских
объектов, которые не известны модулю logging. Например,
рассмотрим logging.handlers.MemoryHandler, который принимает
аргумент target – другой обработчик для делегирования. Поскольку
системе уже известен этот класс, то в конфигурации
указанный target просто должен быть идентификатором объекта соответствующего
целевого обработчика, и система разрешит его в обработчик по идентификатору.
Однако если пользователь определяет my.package.MyHandler, у которого есть
обработчик alternate, система конфигурации не будет знать, что
alternate ссылается на обработчик. Чтобы учесть это, общая
система разрешения позволяет пользователю указать:
handlers:
file:
# здесь задаётся конфигурация файлового обработчика
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 resolution and custom importers
По умолчанию для импорта используется встроенная функция __import__().
Возможно, вы захотите заменить её собственным механизмом импорта:
в таком случае можно заменить атрибут importer класса
DictConfigurator или его суперкласса,
BaseConfigurator. Однако нужно быть
осторожным из-за того, как функции доступны из классов через
дескрипторы. Если вы используете вызываемый объект Python для импорта и
хотите определить его на уровне класса, а не экземпляра, необходимо обернуть
его в staticmethod(). Например:
from importlib import import_module
from logging.config import BaseConfigurator
BaseConfigurator.importer = staticmethod(import_module)
Не нужно оборачивать в staticmethod(), если вы устанавливаете вызываемый объект импорта
на экземпляре конфигуратора.
Формат файла конфигурации¶Configuration file format
Формат конфигурационного файла, который понимает fileConfig(), основан на функциональности configparser. Файл должен содержать разделы с названиями [loggers], [handlers] и [formatters], которые идентифицируют по имени сущности каждого типа, определенные в файле. Для каждой такой сущности существует отдельный раздел, который определяет, как эта сущность настраивается. Так, для регистратора с именем log01 в разделе [loggers] соответствующие детали конфигурации хранятся в разделе [logger_log01]. Аналогично, обработчик с именем hand01 в разделе [handlers] будет иметь свою конфигурацию в разделе с названием [handler_hand01], а форматировщик с именем form01 в разделе [formatters] будет иметь свою конфигурацию в разделе [formatter_form01]. Конфигурация корневого регистратора должна быть задана в разделе [logger_root].
Примечание
API fileConfig() старше, чем API dictConfig(), и не предоставляет функциональности для покрытия некоторых аспектов логирования. Например, вы не можете настроить объекты Filter, которые обеспечивают фильтрацию сообщений, выходящую за рамки простых целочисленных уровней, с помощью fileConfig(). Если вам нужны экземпляры 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 означает, что все сообщения будут
записаны. Значения уровней eval() вычисляются в контексте пространства имён 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] и иметь соответствующий раздел в конфигурационном файле.
The args entry, when eval()uated in the context of the logging
package’s namespace, is the list of arguments to the constructor for the handler
class. Refer to the constructors for the relevant handlers, or to the examples
below, to see how typical entries are constructed. If not provided, it defaults
to ().
The optional kwargs entry, when eval()uated in the context of the
logging package’s namespace, is the keyword argument dict to the constructor
for the handler class. If not provided, it defaults to {}.
[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
datefmt=
class=logging.Formatter
Запись format – это общая строка форматирования, а запись datefmt – это
строка формата даты/времени, совместимая с strftime(). Если она пуста,
пакет подставляет нечто почти эквивалентное указанию строки формата даты
'%Y-%m-%d %H:%M:%S'. Этот формат также задаёт миллисекунды,
которые добавляются к результату использования указанной выше строки формата с запятой
в качестве разделителя. Пример времени в этом формате: 2003-01-23 00:29:50,411.
Запись class является необязательной. Она указывает имя класса форматера
(в виде полного имени модуля и класса через точку.) Этот параметр полезен для создания экземпляра подкласса
Formatter. Подклассы
Formatter могут отображать трассировку исключений в развёрнутом или
сжатом формате.
Примечание
Из-за использования eval(), как описано выше, существуют потенциальные угрозы безопасности, связанные с использованием listen() для отправки и получения конфигураций через сокеты. Риски ограничены случаями, когда несколько пользователей, не доверяющих друг другу, запускают код на одной машине; обратитесь к документации listen() для получения дополнительной информации.
См. также
- Модуль
logging Справочник API для модуля logging.
- Модуль
logging.handlers Полезные обработчики, входящие в состав модуля logging.