logging.config — Конфигурация журналирования


В этом разделе рассматривается API для настройки модуля logging.

Функции конфигурации

Следующие функции настраивают модуль 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)

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

Параметры:
  • fname – Имя файла, или файловый объект, или производный экземпляр из RawConfigParser. Если передаётся экземпляр, производный от RawConfigParser, он используется как есть. В противном случае — Configparser создан экземпляром, а конфигурация, прочитанная им из файла объекта переданная в fname. Если у него есть readline() , предполагается, что он является файловым объектом и читается с использованием read_file(); иначе, предполагается, что это имя файла и передаётся read().
  • defaults – Можно указать значения по умолчанию, которые будут передаваться в ConfigParser в этом аргументе.
  • disable_existing_loggers – Если указано значение False, логгеры, которые существуют при выполнении этого вызова, остаются включенными. По умолчанию — True, потому что он разрешает старое поведение в обратно-совместимый способ. Это поведение отключает любые существующие логгеры без полномочий root, если они или их предки явно названы в конфигурации logging.

Изменено в версии 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 в отправляемой вами конфигурации.

logging.config.stopListening()

Останавливает прослушивающий сервер, созданный с помощью вызова listen(). Обычно она вызывается перед вызовом join() для возвращаемого значения из listen().

Схема словаря конфигурации

Описание конфигурации logging требует перечисления различных объектов, и связей между ними; например, вы можете создать обработчик с именем «console», а затем сказать, что средство ведения журнала с именем «startup» будет отправлять свои сообщения обработчику «console». Эти объекты не ограничиваются теми, которые предоставляются модулем logging, потому что вы можете написать свой собственный класс форматирования или обработчика. Параметры этих классов могут также включать внешние объекты, такие как sys.stderr. Синтаксис для описания этих объектов и соединений определён в Связи объектов ниже.

Подробная информация о схеме словаря

Словарь, переданный в dictConfig(), должен содержать следующие ключи:

  • version — для установки в целочисленное значение, представляющее версию схемы. Единственное допустимое значение в настоящее время — 1, но наличие этого ключа позволяет схеме развиваться, сохраняя при этом обратную совместимость.

Все остальные ключи необязательны, но если они есть, они будут интерпретироваться, как описано ниже. Во всех нижеприведенных случаях, когда упоминается «configuring dict», он будет проверяться на наличие специального ключа '()', чтобы узнать, требуется ли настраиваемое создание экземпляра. Если это так, то для создания экземпляра используется механизм, описанный ниже в Пользовательские объекты; в противном случае контекст используется для определения того, что создавать.

  • formatters — соответствующее значение будет dict, в котором каждый ключ является идентификатором форматтера, а каждое значение — dict, определяет настройку соответствующего экземпляра Formatter.

    В конфигурационном слове ищутся ключи format и datefmt (со значениями по умолчанию None), и они используются для создания экземпляра Formatter.

    Изменено в версии 3.8: Ключ validate (по умолчанию True) можно добавить в раздел formatters конфигурационного dict, это необходимо для проверки формата.

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

    В конфигурационном слове ищется ключ name (по умолчанию — пустая строка), и он используется для создания экземпляра logging.Filter.

  • handlers — соответствующее значение будет dict, в котором каждый ключ является идентификатором обработчика, а каждое значение — dict, определяет настройку экземпляра обработчика.

    В конфигурационном слове ищутся следующие ключи:

    • class (обязательно). Это полное имя класса обработчика.
    • level (необязательно). Уровень обработчика.
    • formatter (необязательно). Идентификатор форматтера для этого обработчика.
    • filters (необязательно). Список идентификаторов фильтров для этого обработчика.

    Все ключи other передаются в конструктор обработчика как ключевые аргументы. Например, учитывая сниппет:

    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 (необязательно). Список идентификаторов фильтров для этого логгера.
    • handlers (необязательно). Список идентификаторов обработчиков для этого логгера.

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

  • root — это будет конфигурация корневого логгера. Обработка конфигурации будет такой же, как и для любого логгера, за исключением того, что параметр propagate не будет применяться.

  • incremental — следует ли интерпретировать конфигурацию как инкрементную к существующей конфигурации. Это значение по умолчанию False, что означает, что указанная конфигурация заменяет существующую конфигурацию с той же семантикой, которая используется существующим API fileConfig().

    Если заданное значение — True, конфигурация обрабатывается, как описано в разделе о Инкрементальная конфигурация.

  • disable_existing_loggers — должны ли быть отключены какие-либо существующие логгеры без полномочий root. Этот параметр отражает одноименный параметр в fileConfig(). В случае отсутствия этот параметр по умолчанию равен True. Это значение игнорируется, если incremental равно True.

Инкрементальная конфигурация

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

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

Таким образом, когда присутствует ключ incremental конфигурационного dict и является True, система полностью проигнорирует любые записи formatters и filters и обработает только настройки level в записях handlers и level и propagate настройки в записях loggers и root .

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

Связи объектов

Схема определяет множество объектов ведения журнала: логгеров, обработчиков, форматтеров, фильтров — которые связаны друг с другом в графе объектов. Таким образом, схема должна представлять связи между объектами. Например, предположим, что после настройки логгер прикрепил к нему определенный обработчик. Для целей этого обсуждения мы можем сказать, что логгер представляет источник, а обработчик — назначение соединения между ними. Конечно, в настроенных объектах это представлено логгером, содержащим ссылку на обработчик. В словаре конфигурации это делается путём присвоения каждому объекту назначения идентификатора, который однозначно его идентифицирует, а затем использования идентификатора в конфигурации исходного объекта, чтобы указать, что существует соединение между источником и объектом назначения с этим идентификатором.

Так, например, рассмотрим следующий фрагмент YAML:

formatters:
  brief:
    # конфигурация для форматтера с идентификатором 'brief' находится здесь
  precise:
    # конфигурация для форматтера с идентификатором 'precise' находится здесь
handlers:
  h1: #Это идентификатор
   # конфигурация обработчика с идентификатором 'h1' находится здесь
   formatter: brief
  h2: #Это другой идентификатор
   # конфигурация обработчика с идентификатором '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.

Пользовательские объекты

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

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

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)

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

Доступ к внешним объектам

Бывают случаи, когда конфигурация должна ссылаться на объекты, внешние по отношению к конфигурации, например 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, необходим более общий механизм. Например, рассмотрим принимающий аргумент target logging.handlers.MemoryHandler, являющийся ещё одним обработчиком для делегирования. Поскольку система уже знает об этом классе, то в конфигурации данный 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: [email protected]
    toaddrs:
      - [email protected]
      - [email protected]
    subject: Houston, we have a problem.

в конфигурации строка 'cfg://handlers' будет разрешена в словарь с ключом handlers, строка 'cfg://handlers.email будет преобразована в словарь с ключом email в dict handlers и так далее. Строка 'cfg://handlers.email.toaddrs[1] преобразуется в 'dev_team.domain.tld', а строка 'cfg://handlers.email.toaddrs[0]' преобразуется в значение '[email protected]'. К значению 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(), если вы устанавливаете вызываемый импорт в конфигураторе instance.

Формат файла конфигурации

Формат файла конфигурации, понимаемый 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] и иметь соответствующий раздел в файле конфигурации.

Запись args, когда eval() в контексте пространства имён пакета logging, представляет собой список аргументов конструктора для класса обработчика. Обратитесь к конструкторам соответствующих обработчиков или к примерам ниже, чтобы увидеть, как создаются типичные записи. Если не указан, по умолчанию используется ().

Необязательная запись kwargs, когда eval() в контексте пространства имён пакета logging, является ключевым аргументом словаря для конструктора класса обработчика. Если не указан, по умолчанию используется {}.

[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.