реклама
Бургер менюБургер меню

Кеннет Рейтц – Автостопом по Python (страница 22)

18

Журналирование бывает двух видов:

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

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

Единственный случай, когда print предпочтительнее журналирования, — если вам нужно отобразить справку для приложения командной строки. Рассмотрим причины, почему журналирование лучше, чем print:

• запись в журнале (https://docs.python.org/library/logging.html#logrecord-attributes), которая создается при каждом событии журналирования, содержит полезную диагностическую информацию вроде имени файла, полного пути, функции и номера строки для события журналирования;

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

• процесс журналирования можно выборочно приостанавливать с помощью метода logging.Logger.setLevel() или отключать путем установки значения атрибута logging.Logger.disabled равным True.

Журналирование для библиотеки

Заметки о конфигурировании журналирования для библиотеки содержатся в руководстве по журналированию (http://bit.ly/configuring-logging). Еще один хороший ресурс с примерами использования журналирования — библиотеки, которые мы упомянем в следующей главе. Поскольку пользователь (а не библиотека) должен указывать, что случится, когда произойдет событие журналирования, мы должны констатировать:

Настоятельно рекомендуется не добавлять никаких обработчиков помимо NullHandler к средствам ведения журнала для библиотек.

Обработчик NullHandler делает то, что указано в его имени, то есть ничего. В противном случае пользователь должен будет самостоятельно отключать журналирование, если оно ему не требуется.

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

Рассмотрим пример применения этого приема в исходном коде библиотеки Requests (https://github.com/kennethreitz/requests) — разместите это в файле верхнего уровня __init__.py вашего проекта:

# Установить дескриптор журналирования по умолчанию для того, чтобы

# избежать появления предупреждений, которые гласят «Обработчик не найден».

import logging

try: # Python 2.7+

····from logging import NullHandler

except ImportError:

····class NullHandler(logging.Handler):

········def emit(self, record):

············pass

logging.getLogger(__name__). addHandler(NullHandler())

Журналирование для приложения

Twelve-Factor App (http://12factor.net/) (авторитетный источник, где перечислены правила хорошего тона, применяемые при разработке приложений) содержит раздел, в котором рассказывается о подобных правилах журналирования (http://12factor.net/logs). В нем предложено рассматривать события журнала как поток событий, для отправки этого потока в стандартный поток вывода нужно использовать среду приложения.

Существует минимум три способа конфигурирования средств ведения журнала (табл. 4.4).

Более подробная информация о формате INI содержится в разделе руководства журналирования, посвященном журналированию конфигурации (https://docs.python.org/howto/logging.html#configuring-logging). Минимальный файл конфигурации будет выглядеть так:

[loggers]

keys=root

[handlers]

keys=stream_handler

[formatters]

keys=formatter

[logger_root]

level=DEBUG

handlers=stream_handler

[handler_stream_handler]

class=StreamHandler

level=DEBUG

formatter=formatter

args=(sys.stderr,)

[formatter_formatter]

format=%(asctime)s %(name)-12s %(levelname)-8s %(message)s

asctime, name, levelname и message являются необязательными атрибутами библиотеки журналирования. Полный список доступных вариантов и их описание смотрите в документации Pytho (http://bit.ly/logrecord-attributes). Предположим, что наша конфигурация журналирования называется logging_conf.ini. Для того чтобы настроить средства ведения журнала с помощью этой конфигурации в коде, используем функцию logging.config.fileconfig():

import logging

from logging.config import fileConfig

fileConfig('logging_config.ini')

logger = logging.getLogger()

logger.debug('often makes a very good meal of %s', 'visiting tourists')

В версии Python 2.7 вы можете использовать словарь с деталями конфигурации. В PEP 391 (https://www.python.org/dev/peps/pep-0391) содержится список обязательных и необязательных элементов словаря конфигурации. Рассмотрим минимальную реализацию:

import logging

from logging.config dictConfig

·

logging_config = dict(

····version = 1,

····formatters = {

········'f': {'format':

············'%(asctime)s %(name)-12s %(levelname)-8s %(message)s' }

········},

····handlers = {

········'h': {'class': 'logging.StreamHandler',

············'formatter': 'f',

············'level': logging.DEBUG}

····loggers = {

········'root': {'handlers': ['h'],