Логирование HOWTO

Автор:Vinay Sajip <vinay_sajip at red-dove dot com>

Общий учебник по логированию

Логирование - это средство отслеживания событий, которые происходят при запуске какого-либо программного обеспечения. Разработчик программного обеспечения добавляет требования логирование к их код, чтобы указать, что определенные события имели место. Событие описывается описательным сообщением, которое может необязательно содержать переменные данные (т.е. данные, которые потенциально различны для каждого события). События также имеют значение, которое разработчик приписывает событию; важность также можно назвать level или severity.

Когда использовать логирование

Логирование предоставляет набор удобных функций для простого использования логирование. Это debug(), info(), warning(), error() и critical(). Чтобы определить, когда следует использовать ведение журнала, см. приведенную ниже таблицу, в которой указано, что для каждой из общих задач лучше всего использовать инструмент.

Задача, которую вы хотите выполнить Лучший инструмент для задачи
Отображение вывода в консоли для обычного использования сценария командной строки или программы print()
Сообщать о событиях, которые происходят во время нормальной работы программы (например, для мониторинга состояния или расследования неисправностей) logging.info() (или logging.debug() для очень подробного вывода для диагностических целей)
Сообщить предупреждение о конкретном событии во время выполнения

warnings.warn() в коде библиотеки, если проблему можно избежать, и клиентское приложение должно быть изменено, чтобы исключить предупреждение

logging.warning() если программа клиента никак не реагирует на ситуацию, но событие всё равно следует зафиксировать

Сообщить об ошибке, связанной с конкретным событием во время выполнения Поднять исключение
Сообщить о подавлении ошибки, не вызывая исключение (например, обработчик ошибок в длительном серверном процессе) logging.error(), logging.exception() или logging.critical() в зависимости от конкретной ошибки и области применения

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

Уровень Когда используется
DEBUG Подробная информация, как правило, интересна только для диагностики проблем.
INFO Подтверждение, что всё работает, как ожидалось.
WARNING Указание на то, что произошло что-то неожиданное или указание на проблему в ближайшем будущем (например, «недостаточно места на диске»). Программное обеспечение всё ещё работает как ожидалось.
ERROR Появление более серьезной проблемы, когда программное обеспечение не смогло выполнить какую-либо функцию.
CRITICAL Серьезная ошибка, указывающая на то, что программа не может дальше продолжать работу.

По умолчанию используется уровень WARNING, что означает, что отслеживаются только события этого уровня и выше, если пакет логирование не настроен на обратное.

Отслеживаемые события могут обрабатываться различными способами. Самый простой способ обработки отслеживаемых событий - печать их на консоли. Другой распространенный способ - записать их в дисковый файл.

Простой пример

Очень простой пример:

import logging
logging.warning('Watch out!')  # will print a message to the console
logging.info('I told you so')  # will not print anything

Если ввести эти строки в сценарий и запустить его, вы увидите:

WARNING:root:Watch out!

распечатано на консоли. Сообщение INFO не отображается, так как используется уровень по умолчанию WARNING. Печатное сообщение включает в себя указание уровня и описание события, предоставленного в логирование вызове, то есть «Берегись!». Пока не беспокойтесь о «корневой» части: она будет объяснена позже. Фактические выходные данные могут быть отформатированы достаточно гибко, если это необходимо; параметры форматирования также будут объяснены ниже.

Логирование в файл

Очень распространенная ситуация - запись событий логирование в файл, так что давайте посмотрим на это дальше. Обязательно попробуйте выполнить следующие действия в только что запущенном интерпретаторе Python и не продолжайте работу из вышеописанного сеанса:

import logging
logging.basicConfig(filename='example.log',level=logging.DEBUG)
logging.debug('This message should go to the log file')
logging.info('So should this')
logging.warning('And this, too')

И теперь, если мы откроем файл и посмотрим, что у нас есть, мы должны найти сообщения журнала:

DEBUG:root:This message should go to the log file
INFO:root:So should this
WARNING:root:And this, too

Этот пример также показывает, как вы можете установить уровень логирование, который действует как порог для отслеживания. В этом случае, поскольку мы установили пороговое значение DEBUG, все сообщения были напечатаны.

Если требуется задать уровень логирование из опции командной строки, например:

--log=INFO

и у вас есть значение параметра, переданное для --log в некоторых переменных loglevel, вы можете использовать:

getattr(logging, loglevel.upper())

для получения значения, которое будет передано basicConfig() через аргумент level. Возможно, потребуется проверить любое введенное пользователем значение по ошибке, как в следующем примере:

# assuming loglevel is bound to the string value obtained from the
# command line argument. Convert to upper case to allow the user to
# specify --log=DEBUG or --log=debug
numeric_level = getattr(logging, loglevel.upper(), None)
if not isinstance(numeric_level, int):
    raise ValueError('Invalid log level: %s' % loglevel)
logging.basicConfig(level=numeric_level, ...)

Требование к basicConfig() должно произойти before любые требования к debug(), info() и т.д. Как он задуман как одноразовое простое средство конфигурирования, только первый вызов фактически сделает что-либо: последующие вызовы фактически не являются операционными.

При выполнении вышеуказанного сценария несколько раз сообщения из последовательных прогонов добавляются к файлу example.log. Если необходимо, чтобы каждый прогон начинался заново, не вспоминая сообщения из предыдущих прогонов, можно указать аргумент filemode, изменив вызов в приведенном выше примере на:

logging.basicConfig(filename='example.log', filemode='w', level=logging.DEBUG)

Выходные данные будут такими же, как и ранее, но файл журнала больше не добавляется, поэтому сообщения из предыдущих прогонов будут потеряны.

Логирование из нескольких модулей

Если ваша программа состоит из нескольких модулей, вот пример того, как вы могли бы организовать логирование в ней:

# myapp.py
import logging
import mylib

def main():
    logging.basicConfig(filename='myapp.log', level=logging.INFO)
    logging.info('Started')
    mylib.do_something()
    logging.info('Finished')

if __name__ == '__main__':
    main()
# mylib.py
import logging

def do_something():
    logging.info('Doing something')

Если запустить myapp.py, вы увидите это в myapp.log:

INFO:root:Started
INFO:root:Doing something
INFO:root:Finished

это, надеюсь, то, что вы ожидали увидеть. Это можно обобщить для нескольких модулей, используя шаблон в mylib.py. Обратите внимание, что для этой простой модели использования, вы не знаете, глядя в файл журнала, where в приложении ваши сообщения пришли, помимо просмотра описания события. Чтобы отследить расположение сообщений, необходимо обратиться к документации за пределами учебного уровня - см. раздел Продвинутый учебник по логированию.

Данные переменной логирования

Для регистрации переменных данных используйте формат строка для сообщения описания события и добавьте переменные данные в качестве аргументов. Например:

import logging
logging.warning('%s before you %s', 'Look', 'leap!')

будет display:

WARNING:root:Look before you leap!

Как видно, при объединении переменных данных в сообщение описания события используется старый,% -стиль форматирования строка. Это для обратной совместимости: пакет логирование предварительно датирует новые параметры форматирования, такие как str.format() и string.Template. Эти более новые параметры форматирования поддержанный are, но исследование их вне область видимости этой обучающей программы: см. Использование определенных стилей форматирования в приложении для получения дополнительной информации.

Изменение формата отображаемых сообщений

Чтобы изменить формат, который является используемый, чтобы показать сообщения, вы должны определить формат, который вы хотите использовать:

import logging
logging.basicConfig(format='%(levelname)s:%(message)s', level=logging.DEBUG)
logging.debug('This message should appear on the console')
logging.info('So should this')
logging.warning('And this, too')

который был бы print:

DEBUG:This message should appear on the console
INFO:So should this
WARNING:And this, too

Обратите внимание, что «корень», появившийся в предыдущих примерах, исчез. Для получения полного набора вещей, которые могут отображаться в строках формата, можно обратиться к документации для LogRecord атрибуты, но для простого использования необходимо только levelname (серьезность), message (описание события, включая переменные данные) и, возможно, для отображения при возникновении события. Это описано в следующем разделе.

Отображение даты/времени в сообщениях

Для отображения даты и времени события необходимо поместить «% (asctime) s» в строку формата:

import logging
logging.basicConfig(format='%(asctime)s %(message)s')
logging.warning('is when this event was logged.')

который должен напечатать что-то вроде:

2010-12-12 11:41:42,612 is when this event was logged.

Формат по умолчанию для отображения даты/времени (показан выше) подобен ISO8601 или RFC 3339. Если требуется дополнительное управление форматированием даты/времени, укажите аргумент datefmt для basicConfig, как в данном примере:

import logging
logging.basicConfig(format='%(asctime)s %(message)s', datefmt='%m/%d/%Y %I:%M:%S %p')
logging.warning('is when this event was logged.')

который будет отображать что-то вроде:

12/12/2010 11:46:36 AM is when this event was logged.

Формат аргумента datefmt совпадает с форматом, поддерживаемым time.strftime().

Следующие шаги

На этом основное учебное пособие завершается. Этого должно быть достаточно, чтобы поднять тебя и побежать с логирование. Есть гораздо больше, что предлагает пакет логирование, но чтобы извлечь из него максимум пользы, вам нужно потратить немного больше времени на чтение следующих разделов. Если вы готовы к этому, возьмите немного любимого напитка и продолжайте.

Если ваши потребности в логирование просты, то используйте приведенные выше примеры, чтобы включить логирование в свои собственные сценарии, и если вы сталкиваетесь с проблемами или чего-то не понимаете, пожалуйста, запишите вопрос в группу comp.lang.python Usenet (доступна по адресу https://groups.google.com/forum/ #! forum/comp.lang.python), и вы должны получить помощь до слишком длительного времени.

Все еще здесь? вы можете продолжить чтение следующих разделов, которые предоставляют несколько более продвинутое/углубленное учебное пособие, чем базовое выше. После этого можно взглянуть на Поваренная книга логирования.

Продвинутый учебник по логированию

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

  • Логгеры выставляют интерфейс, который непосредственно использует заявление код.
  • Обработчики отправляют записи журнала (созданные регистраторами) в соответствующее место назначения.
  • Фильтры обеспечивают более тонкое зернистое средство для определения, какие записи журнала следует выводить.
  • Форматеры определяют формат записей журнала в окончательном выводе.

Информация о событиях журнала передается между регистраторами, обработчиками, фильтрами и форматерами в LogRecord сущность.

Логирование выполнен, назвав методы на сущности Logger класс (после этого названным loggers). Каждый сущность имеет имя, и они концептуально расположены в иерархии пространства имен с использованием точек (периодов) в качестве разделителей. Например, журнал с именем scan является родителем журналов scan.text, scan.html и scan.pdf. Имена регистраторов могут быть любыми, какими угодно, и указывать область приложения, в которой создается зарегистрированное сообщение.

Хорошим соглашением для использования при именовании регистраторов является использование регистратора на уровне модуля в каждом модуле, использующем регистрацию, названном следующим образом:

logger = logging.getLogger(__name__)

Это означает, что имена регистраторов отслеживают иерархию пакетов/модулей и интуитивно очевидно, где события регистрируются только из имени регистратора.

Корень иерархии регистраторов называется корневым регистратором. Это логгер используемый по функциям debug(), info(), warning(), error() и critical(), которые просто называют одноименную метод корневого логгера. Функции и методы имеют одинаковые подписи. Имя корневого регистратора печатается как «root» в выводе журнала.

Конечно, можно регистрировать сообщения в разных адресатах. Поддержка включена в пакет для записи сообщений журнала в файлы, расположения HTTP GET/POST, электронной почты через SMTP, общих сокетов, очередей или специфичных для оС механизмов логирование, таких как syslog или журнал событий Windows NT. Пункты назначения обслуживаются handler классы. Вы можете создать свое собственное место назначения регистрации класс, если у вас есть особые требования, не отвечавшие каким-либо встроенным укладчиком классы.

По умолчанию ни для одного сообщения логирование не задано назначение. Можно указать место назначения (например, консоль или файл) с помощью basicConfig(), как в примерах учебных пособий. При вызове функций debug(), info(), warning(), error() и critical() они проверяют, не задан ли адресат; и если он не установлен, они задают место назначения консоли (sys.stderr) и формат по умолчанию для отображаемого сообщения перед делегированием корневому регистратору для выполнения фактического вывода сообщения.

Формат по умолчанию, задаваемый basicConfig() для сообщений:

severity:logger name:message

Это можно изменить, передав формат строка в basicConfig() с аргументом format ключевой. Все параметры построения формата строка см. в разделе Объекты Formatter.

Поток логирование

Поток информации о событиях регистрации в регистраторах и обработчиках показан на следующей схеме.

../_images/logging_flow.png

Логгеры

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

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

Это наиболее распространенные методы настройки:

  • Logger.setLevel() указывает сообщение журнала наименьшей степени серьезности, обрабатываемое регистратором, где debug - самый низкий встроенный уровень серьезности, а critical - самый высокий встроенный уровень серьезности. Например, если уровень серьезности - INFO, регистратор будет обрабатывать только сообщения INFO, WARNING, ERROR и CRITICAL и игнорировать сообщения DEBUG.
  • Logger.addHandler() и Logger.removeHandler() добавляют и удаляют объекты обработчика из объекта регистратора. Обработчики подробно описаны в разделе Обработчики.
  • Logger.addFilter() и Logger.removeFilter() добавление и удаление объектов фильтра из объекта регистратора. Более подробно фильтры описаны в разделе Объекты фильтра.

Не нужно всегда вызывать эти методы на каждом созданном регистраторе. См. последние два абзаца в этом разделе.

С настроенным объектом регистратора следующие методы создают сообщения журнала:

  • Logger.debug(), Logger.info(), Logger.warning(), Logger.error() и Logger.critical() все создают записи журнала с сообщением и уровнем, соответствующим их соответствующим именам метод. Сообщение - на самом деле последовательность формата, которая может содержать стандартный синтаксис замены строка %s, %d, %f, и так далее. Остальные аргументы представляют собой список объектов, соответствующих полям подстановки в сообщении. Что касается **kwargs, то логирование методы заботятся только о ключевой exc_info и используют его для определения того, следует ли регистрировать информацию об исключениях.
  • Logger.exception() создает сообщение журнала, похожее на Logger.error(). Разница в том, что Logger.exception() сбрасывает трассировку стека вместе с ней. Вызовите эту метод только из обработчика исключений.
  • Logger.log() принимает уровень журнала в качестве явного аргумента. Это немного более подробно для сообщений логирование, чем использование методы удобства уровня журнала, перечисленных выше, но это способ регистрации на пользовательских уровнях журнала.

getLogger() возвращает ссылку на регистратор сущность с указанным именем, если оно указано, или root, если нет. Имена являются разделенными по периодам иерархическими структурами. При нескольких вызовах getLogger() с одним и тем же именем будет возвращена ссылка на один и тот же объект регистратора. логгеры, которые далее вниз в иерархическом списке являются потомками регистраторов выше в списке. Например, для логгера с именем foo все логгеры с именами foo.bar, foo.bar.baz и foo.bam являются потомками foo.

У логгеров есть понятие effective level. Если уровень явно не установлен на логере, уровень его родителя - используемый вместо этого как его эффективный уровень. Если у родителя нет явного набора уровня, родитель its исследован, и так далее - все предки - найденный, пока явно установленный уровень не найден. Корневой журнал всегда имеет явный набор уровней (по умолчанию - WARNING). Решая, обработать ли событие, эффективный уровень логера - используемый, чтобы определить, передано ли событие укладчикам логера.

Дочерние логгеры распространяют сообщения до обработчиков, связанных с их предками. Из-за этого нет необходимости определять и настраивать обработчики для всех регистраторов, используемых приложением. Достаточно настроить обработчики для журнала верхнего уровня и создать при необходимости дочерние логгеры. (Вы можете, однако, выключить распространение, установив propagate атрибут логера к False.)

Обработчики

Handler объекты отвечают за отправку соответствующих сообщений журнала (на основе серьезности сообщений журнала) указанному адресату обработчика. Объекты Logger могут добавлять к себе ноль или более объектов обработчика с помощью addHandler() метод. Например, приложению может потребоваться отправить все сообщения журнала в файл журнала, все сообщения журнала с ошибками или выше для stdout, а также все сообщения, имеющие решающее значение для адреса электронной почты. Этот сценарий требует трех отдельных обработчиков, каждый из которых отвечает за отправку сообщений определенной степени серьезности в определенное местоположение.

Стандартная библиотека включает довольно много типов обработчиков (см. Полезные обработчики); в примерах учебных пособий используются в основном StreamHandler и FileHandler.

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

  • Метод setLevel(), как и в объектах регистратора, определяет наименьшую степень серьезности, которая будет отправлена соответствующему адресату. Почему есть два метода setLevel()? уровень, установленный в журнале, определяет степень серьезности сообщений, передаваемых обработчикам. Уровень, заданный в каждом обработчике, определяет, какие сообщения будет отправлять обработчик.
  • setFormatter() выбирает объект Formatter для этого обработчика.
  • addFilter() и removeFilter() соответственно конфигурируют и отменяют настройку объектов фильтра на обработчиках.

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

Форматтеры

Объекты Formatter конфигурируют окончательный порядок, структуру и содержимое сообщения журнала. В отличие от основного класса logging.Handler, заявление код может иллюстрировать примерами классы средства форматирования, хотя вы могли вероятный подкласс средство форматирования, если для вашего заявления нужно специальное поведение. Конструктор принимает три дополнительных аргумента: строку формата сообщения, строка формата даты и индикатор стиля.

logging.Formatter.__init__(fmt=None, datefmt=None, style='%')

Если строка формата сообщения отсутствует, по умолчанию используется необработанное сообщение. Если строка формата даты отсутствует, формат даты по умолчанию:

%Y-%m-%d %H:%M:%S

с миллисекундами, зафиксированными в конце. style является одним из «%», «{» или «$». Если один из них не указан, «%» будет используемый.

Если style - „%“, формат строка сообщения использует разработанную замену %(<dictionary key>)s строка; возможные ключи задокументированы в разделе LogRecord атрибуты. Если стиль „{„, формат строка сообщения, как предполагается, совместим с str.format() (использующий аргументы ключевой), в то время как, если стиль - „$“ тогда, формат строка сообщения должен соответствовать тому, что ожидается string.Template.substitute().

Изменено в версии 3.2: Добавлен параметр style.

Следующий формат сообщения строка будет регистрировать время в удобочитаемом для человека формате, степень серьезности сообщения и содержимое сообщения в этом порядке:

'%(asctime)s - %(levelname)s - %(message)s'

Форматеры используют настраиваемую пользователем функцию для преобразования времени создания записи в кортеж. По умолчанию используется time.localtime(); чтобы изменить это для особого случая средства форматирования, установите converter атрибут сущность к функции с тем же сигнатура как time.localtime() или time.gmtime(). Чтобы измениться оно для всех средств форматирования, например, если вы хотите, чтобы все времена логирование были показаны в GMT, установило converter атрибут в средстве форматирования класс (к time.gmtime для дисплея GMT).

Настройка логирования

Программисты могут настраивать логирование тремя способами:

  1. Создавая лесорубов, укладчиков и средства форматирования, явно используя Python код, который называет конфигурацию методы упомянутый выше.
  2. Создание файла логирование config и его чтение с помощью функции fileConfig().
  3. Создание словаря конфигурационной информации и передача его функции dictConfig().

Справочную документацию по последним двум опциям см. в разделе Функции конфигурации. В следующем примере выполняется настройка очень простого регистратора, обработчика консоли и простого формататора с использованием кода Python:

import logging

# create logger
logger = logging.getLogger('simple_example')
logger.setLevel(logging.DEBUG)

# create console handler and set level to debug
ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)

# create formatter
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')

# add formatter to ch
ch.setFormatter(formatter)

# add ch to logger
logger.addHandler(ch)

# 'application' code
logger.debug('debug message')
logger.info('info message')
logger.warning('warn message')
logger.error('error message')
logger.critical('critical message')

Запуск этого модуля из командной строки приводит к следующим выводам:

$ python simple_logging_module.py
2005-03-19 15:10:26,618 - simple_example - DEBUG - debug message
2005-03-19 15:10:26,620 - simple_example - INFO - info message
2005-03-19 15:10:26,695 - simple_example - WARNING - warn message
2005-03-19 15:10:26,697 - simple_example - ERROR - error message
2005-03-19 15:10:26,773 - simple_example - CRITICAL - critical message

Следующий модуль Python создает регистратор, обработчик и форматер, почти идентичные тем, которые приведены в приведенном выше примере, с единственным отличием от имен объектов:

import logging
import logging.config

logging.config.fileConfig('logging.conf')

# create logger
logger = logging.getLogger('simpleExample')

# 'application' code
logger.debug('debug message')
logger.info('info message')
logger.warning('warn message')
logger.error('error message')
logger.critical('critical message')

Вот файл логирование.conf:

[loggers]
keys=root,simpleExample

[handlers]
keys=consoleHandler

[formatters]
keys=simpleFormatter

[logger_root]
level=DEBUG
handlers=consoleHandler

[logger_simpleExample]
level=DEBUG
handlers=consoleHandler
qualname=simpleExample
propagate=0

[handler_consoleHandler]
class=StreamHandler
level=DEBUG
formatter=simpleFormatter
args=(sys.stdout,)

[formatter_simpleFormatter]
format=%(asctime)s - %(name)s - %(levelname)s - %(message)s
datefmt=

Выходные данные почти идентичны выходным данным примера, не основанного на файле конфигурации:

$ python simple_logging_config.py
2005-03-19 15:38:55,977 - simpleExample - DEBUG - debug message
2005-03-19 15:38:55,979 - simpleExample - INFO - info message
2005-03-19 15:38:56,054 - simpleExample - WARNING - warn message
2005-03-19 15:38:56,055 - simpleExample - ERROR - error message
2005-03-19 15:38:56,130 - simpleExample - CRITICAL - critical message

Можно видеть, что файловый подход config имеет несколько преимуществ по сравнению с подходом Python код, главным образом разделение конфигурации и код и способность некодеров легко изменять свойства логирование.

Предупреждение

Функция fileConfig() принимает параметр по умолчанию, disable_existing_loggers, который по умолчанию имеет значение True из-за обратной совместимости. Это может быть или не быть нужным, так как это приведет к отключению некорневых регистраторов, существующих до вызова fileConfig(), если они (или предок) не будут явно названы в конфигурации. Пожалуйста, обратитесь к справочной документации для получения дополнительной информации и определите False для этого параметра, если вы желаете.

Словарь, переданный dictConfig(), может также указывать логическое значение с ключом disable_existing_loggers, которое, если не указано явно в словаре, также по умолчанию интерпретируется как True. Это приводит к описанному выше поведению отключения логгера, которое, возможно, не является желаемым - в этом случае ключ явно должен иметь значение False.

Обратите внимание, что имена класс, на которые ссылаются в конфигурационных файлах, должны быть либо относительно модуля логирование, либо абсолютными значениями, которые могут быть разрешены с помощью обычных механизмов импорта. Таким образом, можно использовать либо WatchedFileHandler (относительно модуля логирование), либо mypackage.mymodule.MyHandler (для класс, определенного в пакете mypackage и модуле mymodule, где mypackage доступен на пути импорта Python).

В Python 3.2 введено новое средство конфигурирования логирование, использующее словари для хранения конфигурационной информации. Это обеспечивает сверхнабор функциональных возможностей подхода на основе конфигурационных файлов, описанного выше, и является рекомендуемым метод конфигурации для новых приложений и развертываний. Поскольку словарь Python - используемый, чтобы содержать информацию конфигурации, и так как вы можете населить тот словарь, используя различные средства, у вас есть больше возможностей для конфигурации. Например, для заполнения конфигурационного словаря можно использовать конфигурационный файл в формате JSON или, при наличии доступа к функциям обработки YAML, файл в формате YAML. Или, конечно, можно построить словарь в коде Python, получить его в маринованном виде через сокет или использовать любой подход, имеющий смысл для вашего приложения.

Вот пример той же конфигурации, что и выше, в формате YAML для нового словарного подхода:

version: 1
formatters:
  simple:
    format: '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
handlers:
  console:
    class: logging.StreamHandler
    level: DEBUG
    formatter: simple
    stream: ext://sys.stdout
loggers:
  simpleExample:
    level: DEBUG
    handlers: [console]
    propagate: no
root:
  level: DEBUG
  handlers: [console]

Для получения дополнительной информации о логирование, используя словарь, см. Функции конфигурации.

Что происходит, если конфигурация не предоставляется

Если конфигурация логирование не предусмотрена, возможно наличие ситуации, когда событие логирование должно быть выведено, но обработчики не могут быть найдены для вывода события. Поведение пакета логирование в этих обстоятельствах зависит от версии Python.

Для версий Python до 3.2 поведение следующее

  • Если logging.raiseExceptions является False (производственный режим), событие отбрасывается без предупреждения.
  • Если logging.raiseExceptions является True (режим разработки), сообщение «Не удалось найти обработчики для регистратора X.Y.Z» печатается один раз.

В Python 3.2 и позднее поведение выглядит следующим образом

  • Событие выводится с помощью обработчика последней инстанции, хранящегося в файле logging.lastResort. Этот внутренний обработчик не связан ни с одним регистратором и действует как StreamHandler, который записывает сообщение описания события в текущее значение sys.stderr (следовательно, с учетом любых перенаправлений, которые могут быть в действии). Форматирование сообщения не выполняется - печатается только сообщение с описанием события. Уровень обработчика установлен равным WARNING, поэтому будут выводиться все события с этой и большей степенью серьезности.

Чтобы получить пред3.2 поведения, logging.lastResort может быть установлен в None.

Настройка логирования для библиотеки

При разработке библиотеки, использующей ведение журнала, следует иметь в виду, как библиотека использует логирование - например, имена регистраторов используемый. Необходимо также уделить определенное внимание его логирование конфигурации. Если использующее приложение не использует ведение журнала, а библиотека код выполняет вызовы логирование, то (как описано в предыдущем разделе) события степени серьезности WARNING и больше будут распечатаны для sys.stderr. Это считается лучшим поведением по умолчанию.

Если по какой-либо причине вы не хотите, чтобы эти сообщения печатались в отсутствие какой-либо конфигурации логирование, вы можете подключить обработчик do-nothing к журналу верхнего уровня для вашей библиотеки. Это позволяет избежать печати сообщения, так как обработчик всегда будет найден для событий библиотеки: он просто не производит никаких выходных данных. Если пользователь библиотеки настроит логирование для прикладного использования, то по-видимому, та конфигурация добавит некоторых укладчиков, и если уровни будут соответственно настроены тогда, то звонки логирование, сделанные в библиотеке код, пошлют продукцию тем укладчикам, как нормальную.

Обработчик do-nothing включен в пакет логирование: NullHandler (с Python 3.1). сущность этого укладчика мог быть добавлен к лесорубу верхнего уровня пространства имен логирование используемый библиотекой (если, вы хотите предотвратить зарегистрированные события своей библиотеки, производимые к sys.stderr в отсутствие конфигурации логирование). Если все логирование библиотеки foo выполняется с помощью регистраторов с именами, соответствующими „foo.x“, „foo.x.y“ и т.д., то код:

import logging
logging.getLogger('foo').addHandler(logging.NullHandler())

должен иметь желаемый эффект. Если организация создает несколько библиотек, то указанное имя регистратора может быть «orgname.foo», а не просто «foo».

Примечание

Настоятельно рекомендуется что вы не добавляйте никаких обработчиков, кроме NullHandler ваша библиотеки логирования. Это связано с тем, что настройка обработчиков является прерогативой разработчика приложения, который использует вашу библиотеку. Разработчик приложения знает свою целевую аудиторию и то, какие обработчики наиболее подходят для их приложения: если добавить обработчики «под капотом», вы вполне можете помешать их способности проводить модульные тесты и доставлять журналы, соответствующие их требованиям.

Уровни логировани

Числовые значения уровней логирование приведены в следующей таблице. Они в первую очередь представляют интерес, если требуется определить собственные уровни и иметь определенные значения относительно предварительно определенных уровней. При определении уровня с тем же числовым значением он перезаписывает предопределенное значение; предварительно определенное имя потеряно.

Уровень Цифровое значение
CRITICAL 50
ERROR 40
WARNING 30
INFO 20
DEBUG 10
NOTSET 0

Уровни также могут быть связаны с регистраторами, устанавливаемыми либо разработчиком, либо загрузкой сохраненной конфигурации логирование. При вызове логирование метод для регистратора его собственный уровень сравнивается с уровнем, связанным с вызовом метод. Если уровень регистратора выше, чем уровень метод вызова, сообщение логирование фактически не генерируется. Это основной механизм, контролирующий детализацию выхода логирование.

Сообщения логирование - кодированный как сущности LogRecord класс. Когда регистратор решает записать событие в журнал, на основе сообщения LogRecord создается сущность логирование.

Сообщения логирование подвергнуты механизму отправки с помощью handlers, которые являются сущности подклассы Handler класс. Укладчики ответственны за обеспечение, что зарегистрированное сообщение (в форме LogRecord) оказывается в конкретном местоположении (или набор местоположений), который полезен для целевой аудитории для того сообщения (такого как конечные пользователи, штат службы поддержки, системные администраторы, разработчики). Укладчики - переданный LogRecord сущности, предназначенный для конкретных мест назначения. Каждый регистратор может иметь нуль, один или несколько обработчиков, связанных с ним (через addHandler() метод Logger). В дополнение к любым обработчикам, непосредственно логера с регистратором, все обработчики, связанные со всеми предками регистратора вызываются для отправки сообщения (если для флага propagate для регистратора не установлено значение false, в этот момент передача обработчикам предков прекращается).

Так же, как и для регистраторов, обработчики могут иметь уровни, связанные с ними. Уровень обработчика действует как фильтр так же, как и уровень регистратора. Если укладчик решает на самом деле послать событие, emit() метод - используемый, чтобы послать сообщение в его место назначения. Большинству пользовательских подклассы Handler потребуется переопределить этот emit().

Пользовательские уровни

Определение собственных уровней возможно, но не должно быть необходимым, поскольку существующие уровни были выбраны на основе практического опыта. Однако, если вы убеждены, что вам нужны пользовательские уровни, следует проявлять большую осторожность при этом, и это, возможно, очень плохая идея для определения пользовательских уровней, если вы разрабатываете библиотеку. Поэтому, если несколько авторов библиотеки, все определяют их собственные уровни, есть шанс, что продукция логирование из таких нескольких библиотек, которыми используемый вместе будет трудным для разработчика использования управлять и/или интерпретировать, потому что данное числовое значение могло бы означать разные вещи для различных библиотек.

Полезные обработчики

Помимо базового класса Handler предусмотрено множество полезных подклассы:

  1. StreamHandler сущности отправлять сообщения потокам (файловым объектам).
  2. FileHandler сущности отправлять сообщения в дисковые файлы.
  3. BaseRotatingHandler является базовым класс для обработчиков, которые поворачивают файлы журнала в определенной точке. Он не предназначен для непосредственного создания экземпляра. Вместо этого используйте RotatingFileHandler или TimedRotatingFileHandler.
  4. RotatingFileHandler сущности отправлять сообщения в дисковые файлы с поддержкой максимального размера файлов журнала и ротации файлов журнала.
  5. TimedRotatingFileHandler сущности отправлять сообщения в дисковые файлы, чередуя файл журнала через определенные промежутки времени.
  6. SocketHandler сущности посылает сообщения в TCP/IP сокеты. Начиная с версии 3.4 поддерживаются также сокеты домена Unix.
  7. DatagramHandler сущности посылает сообщения в UDP сокеты. Начиная с версии 3.4 поддерживаются также сокеты домена Unix.
  8. SMTPHandler сущности отправлять сообщения на указанный адрес электронной почты.
  9. SysLogHandler сущности отправлять сообщения демону системного журнала Unix, возможно, на удаленном компьютере.
  10. NTEventLogHandler сущности посылает сообщения в Windows NT/2000/XP журнал событий.
  11. MemoryHandler сущности отправлять сообщения в буфер в памяти, который очищается при выполнении определенных критериев.
  12. HTTPHandler сущности отправлять сообщения на HTTP-сервер с помощью семантики GET или POST.
  13. WatchedFileHandler сущности смотрит файл, к которому они являются логирование. Если файл изменяется, он закрывается и открывается повторно с использованием имени файла. Этот обработчик полезен только в Unix-подобных системах; Windows не поддерживает базовый механизм используемый.
  14. QueueHandler сущности отправлять сообщения в очередь, например, сообщения, реализованные в модулях queue или multiprocessing.
  15. NullHandler сущности ничего не делать с сообщениями об ошибках. Они используемый разработчиками библиотек, которые хотят использовать ведение журнала, но хотят избежать сообщения «No handlers be found for logger XXX», которое может отображаться, если пользователь библиотеки не настроил логирование. Дополнительные сведения см. в разделе Настройка логирования для библиотеки.

Добавлено в версии 3.1: NullHandler класс.

Добавлено в версии 3.2: QueueHandler класс.

NullHandler, StreamHandler и FileHandler классы определены в основном пакете логирование. Другие обработчики определены в субмодуле, logging.handlers. (Существует также другой подмодуль, logging.config, для функциональности конфигурации.

Зарегистрированные сообщения отформатированы для представления через сущности Formatter класс. Они инициализируются форматом строка, подходящим для использования с оператором% и словарем.

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

Когда фильтрация на основе уровня логера и/или уровня укладчика недостаточно, сущности Filter может быть добавлен и к Logger и к Handler сущности (через их метод addFilter()). Прежде чем принимать решение о дальнейшей обработке сообщения, как логгеры, так и обработчики обращаются ко всем своим фильтрам за разрешением. Если какой-либо фильтр возвращает значение false, сообщение не обрабатывается дальше.

Основная функциональность Filter позволяет фильтровать определенным именем логера. Если эта функция используется, сообщения, отправленные именованному журналу и его потомкам, разрешаются через фильтр, а все остальные удаляются.

Исключения, возникшие во время логирования

Пакет логирование разработан, чтобы глотать исключения, которые происходят в то время как логирование в производстве. Это - то, так, чтобы ошибки, которые происходят, обращаясь с событиями логирование - такими как неверная конфигурация логирование, сеть или другие подобные ошибки - не вызывали применение, используя логирование, чтобы закончиться преждевременно.

SystemExit и KeyboardInterrupt исключения никогда не проглатываются. Другие исключения, которые происходят во время emit() метод Handler подкласс, переданы к его handleError() метод.

Реализация по умолчанию handleError() in Handler проверяет, установлена ли переменная уровня модуля raiseExceptions. Если установлено значение, трейсбэк печатается как sys.stderr. Если не задано, исключение проглатывается.

Примечание

Значение по умолчанию raiseExceptions равно True. Это связано с тем, что во время разработки обычно требуется получать уведомления о любых возникающих исключениях. Рекомендуется, что вы устанавливаете raiseExceptions в False для производственного использования.

Использование произвольных объектов в качестве сообщений

В предыдущих разделах и примерах предполагалось, что сообщение, переданное при логирование события, является строка. Однако это не единственная возможность. Можно передать произвольный объект в виде сообщения, и его __str__() метод будет вызван, когда система логирование должна преобразовать его в представление строка. На самом деле, если вы хотите, вы можете избежать вычисления строка представления вообще - например, SocketHandler испускает событие, pickling его и отправляя по проводу.

Оптимизация

Форматирование аргументов сообщения откладывается до тех пор, пока его не удастся избежать. Однако вычисление аргументов, переданных логирование метод, также может быть дорого, и вы можете избежать этого, если регистратор просто выбросит ваше событие. Чтобы решить, что сделать, вы можете назвать isEnabledFor() метод, который берет аргумент уровня и прибыль, верную, если событие было бы создано лесорубом для того уровня требования. Вы можете написать код как это:

if logger.isEnabledFor(logging.DEBUG):
    logger.debug('Message with %s, %s', expensive_func1(),
                                        expensive_func2())

так что, если порог регистратора установлен выше DEBUG, вызовы expensive_func1() и expensive_func2() никогда не выполняются.

Примечание

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

Есть другая оптимизация, которая может быть сделана для определенных заявлений, для которых нужен более точный контроль над тем, какая информация логирование собрана. Вот список вещей, которые вы можете сделать, чтобы избежать обработки во время логирование, которые вам не нужны:

То, что вы не хотите собирать Как избежать сбора
Информация о том, откуда вызывалось. Установить параметр logging._srcfile в None. Он старается не вызывать sys._getframe(), который может помочь ускорить код в среде поднобной PyPy (который не может ускорить код, который использует sys._getframe()), если и когда PyPy поддерживает Python 3.x.
Потоковая информация. Установить logging.logThreads в 0.
Процессная информация. Установить logging.logProcesses в 0.

Также обратите внимание, что основной модуль логирование только включает основных укладчиков. Если вы не импортируете logging.handlers и logging.config, они не займут память.

См.также

Модуль logging
Ссылка на API для модуля логирование.
Модуль logging.config
Конфигурационное API для логирования.
Модуль logging.handlers
Полезные обработчики, входящие в состав модуля логирования.

Поваренная книга по логированию