Содержание страницы
logging – Средство журналирования для Python¶logging – Logging facility for Python
Этот модуль определяет функции и классы, реализующие гибкую систему логирования ошибок для приложений.
Логирование выполняется вызовом методов экземпляров класса Logger (далее называемых логгерами). Каждый экземпляр имеет имя, и они концептуально организованы в иерархию пространств имён с использованием точек (.) в качестве разделителей. Например, логгер с именем «scan» является родительским для логгеров «scan.text», «scan.html» и «scan.pdf». Имена логгеров могут быть любыми и указывают на область приложения, из которой исходит регистрируемое сообщение.
Регистрируемые сообщения также имеют связанные с ними уровни важности. Предоставляемые по умолчанию уровни: DEBUG, INFO, WARNING, ERROR и CRITICAL. Для удобства уровень важности сообщения указывается вызовом соответствующего метода Logger. Это методы debug(), info(), warning(), error() и critical(), которые соответствуют уровням по умолчанию. Вы не обязаны использовать только эти уровни: можно задать собственные и применять более общий метод Logger – log(), который принимает явный аргумент уровня.
Учебник по журналированию¶Logging tutorial
Ключевое преимущество наличия API логирования, предоставляемого модулем стандартной библиотеки, заключается в том, что все модули Python могут участвовать в логировании, поэтому журнал приложения может включать сообщения от сторонних модулей.
Конечно, можно регистрировать сообщения с разными уровнями подробности или направлять их в разные места. Поддержка записи сообщений в файлы, по HTTP GET/POST, электронной почте через SMTP, через обычные сокеты или через механизмы логирования, специфичные для ОС, – всё это поддерживается стандартным модулем. Вы также можете создать собственный класс назначения журнала, если ваши особые требования не удовлетворяются ни одним из встроенных классов.
Простые примеры¶Simple examples
Большинство приложений, скорее всего, захотят вести журнал в файл, поэтому начнём с этого случая. Используя функцию basicConfig(), можно настроить обработчик по умолчанию так, чтобы отладочные сообщения записывались в файл:
import logging
LOG_FILENAME = '/tmp/logging_example.out'
logging.basicConfig(filename=LOG_FILENAME,level=logging.DEBUG,)
logging.debug('This message should go to the log file')
Теперь, если открыть файл и посмотреть, что в нём, мы должны обнаружить сообщение журнала:
DEBUG:root:This message should go to the log file
Если запускать скрипт многократно, дополнительные сообщения журнала добавляются в конец файла. Чтобы каждый раз создавать новый файл, можно передать аргумент filemode функции basicConfig() со значением 'w'. Вместо того чтобы управлять размером файла самостоятельно, проще использовать RotatingFileHandler:
import glob
import logging
import logging.handlers
LOG_FILENAME = '/tmp/logging_rotatingfile_example.out'
# Настроить конкретный регистратор с нужным уровнем вывода
my_logger = logging.getLogger('MyLogger')
my_logger.setLevel(logging.DEBUG)
# Добавить обработчик сообщений журнала в регистратор
handler = logging.handlers.RotatingFileHandler(
LOG_FILENAME, maxBytes=20, backupCount=5)
my_logger.addHandler(handler)
# Записать несколько сообщений
for i in range(20):
my_logger.debug('i = %d' % i)
# Посмотреть, какие файлы созданы
logfiles = glob.glob('%s*' % LOG_FILENAME)
for filename in logfiles:
print(filename)
В результате должно получиться 6 отдельных файлов, каждый из которых содержит часть истории журнала приложения:
/tmp/logging_rotatingfile_example.out
/tmp/logging_rotatingfile_example.out.1
/tmp/logging_rotatingfile_example.out.2
/tmp/logging_rotatingfile_example.out.3
/tmp/logging_rotatingfile_example.out.4
/tmp/logging_rotatingfile_example.out.5
Самый новый файл всегда называется /tmp/logging_rotatingfile_example.out, и каждый раз, когда он достигает предела размера, он переименовывается с суффиксом .1. Каждый из существующих резервных файлов переименовывается с увеличением суффикса (.1 становится .2 и т.д.), а файл .5 удаляется.
Очевидно, в этом примере максимальный размер файла установлен чрезвычайно малым для наглядности. Следует установить maxBytes в подходящее значение.
Ещё одна полезная возможность API логирования – создавать разные сообщения на разных уровнях логирования. Это позволяет, например, наполнить код отладочными сообщениями, но снизить уровень логирования так, чтобы в рабочей системе эти сообщения не записывались. Уровни по умолчанию: CRITICAL, ERROR, WARNING, INFO, DEBUG и NOTSET.
Логгер, обработчик и вызов сообщения – каждый задаёт свой уровень. Сообщение выводится только в том случае, если обработчик и логгер настроены на вывод сообщений этого уровня или ниже. Например, если сообщение имеет уровень CRITICAL, а логгер настроен на ERROR, сообщение будет выведено. Если сообщение имеет уровень WARNING, а логгер настроен на вывод только ERROR, сообщение не будет выведено:
import logging
import sys
LEVELS = {'debug': logging.DEBUG,
'info': logging.INFO,
'warning': logging.WARNING,
'error': logging.ERROR,
'critical': logging.CRITICAL}
if len(sys.argv) > 1:
level_name = sys.argv[1]
level = LEVELS.get(level_name, logging.NOTSET)
logging.basicConfig(level=level)
logging.debug('This is a debug message')
logging.info('This is an info message')
logging.warning('This is a warning message')
logging.error('This is an error message')
logging.critical('This is a critical error message')
Запустите скрипт с аргументом, например, ‘debug’ или ‘warning’, чтобы увидеть, какие сообщения отображаются на разных уровнях:
$ python logging_level_example.py debug
DEBUG:root:This is a debug message
INFO:root:This is an info message
WARNING:root:This is a warning message
ERROR:root:This is an error message
CRITICAL:root:This is a critical error message
$ python logging_level_example.py info
INFO:root:This is an info message
WARNING:root:This is a warning message
ERROR:root:This is an error message
CRITICAL:root:This is a critical error message
Вы заметите, что во всех этих сообщениях журнала присутствует root. Модуль logging поддерживает иерархию логгеров с разными именами. Простой способ понять, откуда пришло конкретное сообщение, – использовать отдельный объект логгера для каждого модуля. Каждый новый логгер «наследует» конфигурацию своего родителя, а сообщения, отправленные логгеру, включают имя этого логгера. При желании каждый логгер можно настроить по-разному, чтобы сообщения от разных модулей обрабатывались различными способами. Рассмотрим простой пример логирования из разных модулей, чтобы легко было отследить источник сообщения:
import logging
logging.basicConfig(level=logging.WARNING)
logger1 = logging.getLogger('package1.module1')
logger2 = logging.getLogger('package2.module2')
logger1.warning('This message comes from one module')
logger2.warning('And this message comes from another module')
И вывод:
$ python logging_modules_example.py
WARNING:package1.module1:This message comes from one module
WARNING:package2.module2:And this message comes from another module
Существует гораздо больше возможностей для настройки логирования, включая разные варианты форматирования сообщений, доставку сообщений в несколько мест и изменение конфигурации долго работающего приложения на лету через сокетный интерфейс. Все эти возможности подробно описаны в документации библиотечного модуля.
Логгеры¶Loggers
Библиотека logging использует модульный подход и предлагает несколько категорий компонентов: логгеры, обработчики, фильтры и форматировщики. Логгеры предоставляют интерфейс, который напрямую используется кодом приложения. Обработчики отправляют записи журнала в соответствующее место назначения. Фильтры предоставляют более тонкое средство для определения того, какие записи журнала следует передать обработчику. Форматировщики задают структуру итоговой записи журнала.
Объекты Logger выполняют тройную задачу. Во-первых, они предоставляют коду приложения несколько методов, чтобы приложения могли регистрировать сообщения во время выполнения. Во-вторых, объекты логгера определяют, на какие сообщения следует реагировать, на основе степени важности (стандартное средство фильтрации) или объектов-фильтров. В-третьих, объекты логгера передают соответствующие сообщения всем заинтересованным обработчикам.
Наиболее часто используемые методы объектов логгера делятся на две категории: настройка и отправка сообщений.
- Logger.setLevel() задаёт наименьшую степень важности сообщения, которое будет обрабатывать логгер; при этом debug – это наименьший встроенный уровень важности, а critical – наибольший. Например, если уровень важности установлен в info, логгер будет обрабатывать только сообщения info, warning, error и critical, игнорируя сообщения debug.
- 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. Дочерние регистраторы передают сообщения вверх своим родительским регистраторам. Из-за этого нет необходимости определять и настраивать все регистраторы, используемые приложением. Достаточно настроить регистратор верхнего уровня и создавать дочерние регистраторы по мере необходимости.
Обработчики¶Handlers
Объекты Handler отвечают за отправку соответствующих сообщений журнала (на основе их важности) в указанное место назначения. Объекты логгера могут добавлять к себе ноль или более объектов обработчиков с помощью метода addHandler(). В качестве примера: приложение может захотеть отправлять все сообщения в файл журнала, все сообщения уровня error и выше – в stdout, а все сообщения уровня critical – на адрес электронной почты. Для этого требуется три отдельных обработчика, каждый из которых отвечает за отправку сообщений определённого уровня важности в определённое место.
Стандартная библиотека включает довольно много типов обработчиков; в данном учебном пособии используются только StreamHandler и FileHandler.
В обработчике очень мало методов, которые должны беспокоить разработчиков приложений. Единственные методы обработчика, актуальные для разработчиков, использующих встроенные объекты-обработчики (то есть не создающих собственные обработчики), – это следующие методы настройки:
- Метод Handler.setLevel(), как и в объектах регистраторов, задаёт наименьший уровень важности, который будет передан в соответствующее назначение. Зачем нужны два метода setLevel()? Уровень, установленный в регистраторе, определяет, сообщения какой важности он будет передавать своим обработчикам. Уровень, установленный в каждом обработчике, определяет, какие сообщения этот обработчик будет отправлять дальше. setFormatter() выбирает объект Formatter для использования этим обработчиком.
- addFilter() и removeFilter() соответственно добавляют и удаляют объекты фильтров у обработчиков.
Код приложения не должен напрямую создавать экземпляры обработчиков и использовать их. Вместо этого класс Handler является базовым классом, который определяет интерфейс, который должны иметь все обработчики, и устанавливает некоторое поведение по умолчанию, которое дочерние классы могут использовать (или переопределять).
Форматировщики¶Formatters
Объекты Formatter настраивают окончательный порядок, структуру и содержимое сообщения журнала. В отличие от базового класса logging.Handler, код приложения может создавать экземпляры классов форматировщиков, хотя при необходимости особого поведения можно создать подкласс форматировщика. Конструктор принимает два необязательных аргумента: строку формата сообщения и строку формата даты. Если строка формата сообщения не задана, по умолчанию используется исходное сообщение. Если строка формата даты не задана, используется следующий формат даты по умолчанию:
%Y-%m-%d %H:%M:%S
с добавленными в конце миллисекундами.
Строка формата сообщения использует подстановку строк в стиле %(<dictionary key>)s; возможные ключи описаны в разделе Объекты Formatter.
Следующая строка формата сообщения будет записывать время в удобочитаемом формате, уровень серьёзности сообщения и содержимое сообщения в указанном порядке:
"%(asctime)s - %(levelname)s - %(message)s"
Настройка журналирования¶Configuring Logging
Программисты могут настраивать логирование двумя способами: явно создавать логгеры, обработчики и форматтеры в главном модуле с помощью перечисленных выше методов настройки (на Python) или создавая конфигурационный файл логирования. Следующий код – пример настройки очень простого логгера, обработчика консоли и простого форматтера в модуле Python:
import logging
# создать логгер
logger = logging.getLogger("simple_example")
logger.setLevel(logging.DEBUG)
# создать обработчик консоли и установить уровень DEBUG
ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)
# создать форматтер
formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
# добавить форматтер в ch
ch.setFormatter(formatter)
# добавить ch в логгер
logger.addHandler(ch)
# код "application"
logger.debug("debug message")
logger.info("info message")
logger.warn("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")
# создать логгер
logger = logging.getLogger("simpleExample")
# код "application"
logger.debug("debug message")
logger.info("info message")
logger.warn("warn message")
logger.error("error message")
logger.critical("critical message")
Вот файл logging.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
Видно, что подход с файлом конфигурации имеет несколько преимуществ перед подходом с кодом Python: в первую очередь разделение конфигурации и кода, а также возможность для не-программистов легко изменять параметры журналирования.
Настройка журналирования для библиотеки¶Configuring Logging for a Library
При разработке библиотеки, которая использует логирование, необходимо уделить внимание её настройке. Если использующее приложение не использует логирование, а код библиотеки вызывает функции логирования, то в консоль выводится одноразовое сообщение “No handlers could be found for logger X.Y.Z”. Это сообщение предназначено для выявления ошибок в настройке логирования, но может сбить с толку разработчика приложения, который не знает о логировании в библиотеке.
Помимо документирования того, как библиотека использует логирование, хороший способ настроить логирование библиотеки так, чтобы не появлялось ложное сообщение – добавить обработчик, который ничего не делает. Это предотвращает вывод сообщения, поскольку обработчик будет найден, но не производит никакого вывода. Если пользователь библиотеки настраивает логирование для приложения, то, предположительно, эта конфигурация добавит некоторые обработчики, и если уровни настроены правильно, то вызовы логирования в коде библиотеки будут отправлять вывод этим обработчикам, как обычно.
Обработчик, который ничего не делает, можно просто определить следующим образом:
import logging
class NullHandler(logging.Handler):
def emit(self, record):
pass
Экземпляр этого обработчика следует добавить к корневому логгеру пространства имён логирования, используемого библиотекой. Если всё логирование библиотеки foo осуществляется с помощью логгеров с именами вида “foo.x.y”, то код:
import logging
h = NullHandler()
logging.getLogger("foo").addHandler(h)
должен дать желаемый эффект. Если организация выпускает несколько библиотек, то имя логгера можно указать как “orgname.foo” вместо просто “foo”.
Новое в версии 3.1.
Класс NullHandler отсутствовал в предыдущих версиях, но теперь он включён, чтобы его не нужно было определять в коде библиотек.
Уровни журналирования¶Logging Levels
Числовые значения уровней журналирования приведены в следующей таблице. Они представляют интерес в первую очередь, если вы хотите определить собственные уровни и хотите, чтобы они имели определенные значения относительно предопределенных уровней. Если вы определяете уровень с тем же числовым значением, он перезаписывает предопределенное значение; предопределенное имя теряется.
| Уровень | Числовое значение |
|---|---|
| CRITICAL | 50 |
| ERROR | 40 |
| WARNING | 30 |
| INFO | 20 |
| DEBUG | 10 |
| NOTSET | 0 |
Уровни также могут быть связаны с логгерами: они устанавливаются разработчиком или загружаются из сохраненной конфигурации журналирования. Когда на логгере вызывается метод журналирования, логгер сравнивает свой собственный уровень с уровнем, связанным с вызовом метода. Если уровень логгера выше уровня вызова метода, сообщение журналирования фактически не создается. Это базовый механизм, управляющий подробностью вывода журналирования.
Сообщения логирования представляют собой экземпляры класса LogRecord. Когда логгер решает фактически зарегистрировать событие, из сообщения логирования создаётся экземпляр LogRecord.
Сообщения журнала проходят через механизм диспетчеризации с помощью обработчиков, которые являются экземплярами подклассов класса Handler. Обработчики отвечают за то, чтобы записанное сообщение (в виде объекта LogRecord) попало в определённое место (или набор мест), полезное для целевой аудитории этого сообщения (такой как конечные пользователи, сотрудники службы поддержки, системные администраторы, разработчики). Обработчикам передаются экземпляры LogRecord, предназначенные для конкретных назначений. Каждый регистратор может иметь ноль, один или несколько связанных с ним обработчиков (через метод addHandler() класса Logger). В дополнение к любым обработчикам, непосредственно связанным с регистратором, вызываются все обработчики, связанные со всеми предками регистратора, для диспетчеризации сообщения.
Как и для логгеров, для обработчиков могут быть заданы уровни. Уровень обработчика действует как фильтр так же, как и уровень логгера. Если обработчик решает фактически диспетчеризировать событие, используется метод emit() для отправки сообщения в место назначения. Большинству определённых пользователем подклассов Handler потребуется переопределить этот emit().
Полезные обработчики¶Useful Handlers
Помимо базового класса Handler, предоставляется множество полезных подклассов:
- Экземпляры StreamHandler отправляют сообщения об ошибках в потоки (файлоподобные объекты).
- Экземпляры FileHandler отправляют сообщения об ошибках в файлы на диске.
- BaseRotatingHandler – это базовый класс для обработчиков, которые выполняют ротацию файлов журнала в определённый момент. Он не предназначен для прямого создания экземпляров. Вместо этого используйте RotatingFileHandler или TimedRotatingFileHandler.
- Экземпляры RotatingFileHandler отправляют сообщения об ошибках в файлы на диске с поддержкой максимального размера файлов журнала и ротации файлов журнала.
- Экземпляры TimedRotatingFileHandler отправляют сообщения об ошибках в файлы на диске, осуществляя ротацию файла журнала через определённые временные интервалы.
- Экземпляры SocketHandler отправляют сообщения об ошибках в сокеты TCP/IP .
- Экземпляры DatagramHandler отправляют сообщения об ошибках в сокеты UDP .
- Экземпляры SMTPHandler отправляют сообщения об ошибках на указанный адрес электронной почты.
- Экземпляры SysLogHandler отправляют сообщения об ошибках демону syslog Unix , возможно на удалённой машине.
- Экземпляры NTEventLogHandler отправляют сообщения об ошибках в журнал событий Windows NT/2000/XP.
- Экземпляры MemoryHandler отправляют сообщения об ошибках в буфер в памяти, который сбрасывается при выполнении определённых критериев.
- Экземпляры HTTPHandler отправляют сообщения об ошибках на HTTP сервер, используя семантику GET или POST.
- Экземпляры WatchedFileHandler следят за файлом, в который ведут журнал. Если файл изменяется, он закрывается и открывается заново по тому же имени. Этот обработчик полезен только в Unix-подобных системах; Windows не поддерживает используемый механизм.
- Экземпляры NullHandler ничего не делают с сообщениями об ошибках. Они используются разработчиками библиотек, которые хотят использовать логирование, но избежать сообщения “No handlers could be found for logger XXX”, которое может отображаться, если пользователь библиотеки не настроил логирование. См. Настройка логирования для библиотеки для получения дополнительной информации.
Новое в версии 3.1.
Класс NullHandler отсутствовал в предыдущих версиях.
Классы NullHandler, StreamHandler и FileHandler определены в основном пакете logging. Остальные обработчики определены в подмодуле logging.handlers. (Существует также ещё один подмодуль, logging.config, для функциональности настройки.)
Зарегистрированные сообщения форматируются для отображения через экземпляры класса Formatter. Они инициализируются форматной строкой, подходящей для использования с оператором % и словарём.
Для форматирования нескольких сообщений в одном пакете можно использовать экземпляры BufferingFormatter. Помимо строки формата (которая применяется к каждому сообщению в пакете), предусмотрены строки формата для заголовка и завершающей части.
Когда фильтрации на основе уровня логгера и/или уровня обработчика недостаточно, экземпляры Filter можно добавить как к экземплярам Logger, так и к Handler (через их метод addFilter()). Прежде чем решить обрабатывать сообщение дальше, и логгеры, и обработчики запрашивают разрешение у всех своих фильтров. Если любой фильтр возвращает ложное значение, сообщение дальше не обрабатывается.
Базовая функциональность Filter позволяет фильтровать по конкретному имени средства журналирования. Если эта возможность используется, через фильтр пропускаются сообщения, отправленные указанному средству журналирования и его дочерним элементам, а все остальные отбрасываются.
Функции уровня модуля¶Module-Level Functions
В дополнение к классам, описанным выше, существует ряд функций уровня модуля.
- logging.getLogger([name])¶
Возвращает регистратор с указанным именем или, если имя не указано, возвращает регистратор, который является корневым регистратором иерархии. Если указано, имя обычно представляет собой иерархическое имя, разделённое точками, например “a”, “a.b” или “a.b.c.d”. Выбор этих имён полностью зависит от разработчика, использующего логирование.
Все вызовы этой функции с одним и тем же именем возвращают один и тот же экземпляр логгера. Это означает, что экземпляры логгера никогда не нужно передавать между различными частями приложения.
- logging.getLoggerClass()¶
Возвращает либо стандартный класс Logger, либо последний класс, переданный в setLoggerClass(). Эта функция может быть вызвана из определения нового класса, чтобы гарантировать, что установка пользовательского класса Logger не отменит настройки, уже применённые другим кодом. Например:
class MyLogger(logging.getLoggerClass()): # ... переопределить поведение здесь
- logging.debug(msg[, *args[, **kwargs]])¶
Записывает сообщение с уровнем DEBUG в корневой регистратор. msg – это форматная строка сообщения, а args – аргументы, которые объединяются с msg с помощью оператора форматирования строк. (Обратите внимание, что это означает, что в форматной строке можно использовать ключевые слова вместе с единственным аргументом-словарём.)
В kwargs проверяются два именованных аргумента: exc_info, который, если он не равен false, приводит к добавлению информации об исключении в сообщение логирования. Если предоставлен кортеж исключения (в формате, возвращаемом sys.exc_info()), он используется; в противном случае вызывается sys.exc_info() для получения информации об исключении.
Другим необязательным именованным аргументом является extra, который можно использовать для передачи словаря, которым заполняется __dict__ объекта LogRecord, созданного для события логирования, пользовательскими атрибутами. Затем эти пользовательские атрибуты можно использовать по своему усмотрению. Например, их можно включать в логируемые сообщения. Например:
FORMAT = "%(asctime)-15s %(clientip)s %(user)-8s %(message)s" logging.basicConfig(format=FORMAT) d = {'clientip': '192.168.0.1', 'user': 'fbloggs'} logging.warning("Protocol problem: %s", "connection reset", extra=d)
выведет что-то вроде
2006-02-08 22:20:02,165 192.168.0.1 fbloggs Protocol problem: connection reset
Ключи в словаре, переданном в extra, не должны пересекаться с ключами, используемыми системой логирования. (См. документацию Formatter для получения дополнительной информации о том, какие ключи используются системой логирования.)
Если вы решите использовать эти атрибуты в логируемых сообщениях, нужно соблюдать осторожность. Например, в приведённом выше примере Formatter настроен с форматной строкой, которая ожидает 'clientip' и 'user' в словаре атрибутов LogRecord. Если они отсутствуют, сообщение не будет записано, так как возникнет исключение форматирования строки. Поэтому в этом случае всегда нужно передавать словарь extra с этими ключами.
Хотя это может быть раздражающим, эта функция предназначена для использования в специализированных обстоятельствах, таких как многопоточные серверы, где один и тот же код выполняется во многих контекстах, и интересующие условия зависят от этого контекста (например, IP-адрес удалённого клиента и имя аутентифицированного пользователя в приведённом выше примере). В таких обстоятельствах, скорее всего, будут использоваться специализированные Formatter с конкретными Handler.
- logging.info(msg[, *args[, **kwargs]])¶
- Записывает сообщение с уровнем INFO в корневой регистратор. Аргументы интерпретируются так же, как для debug().
- logging.warning(msg[, *args[, **kwargs]])¶
- Регистрирует сообщение с уровнем WARNING в корневом логгере. Аргументы интерпретируются так же, как для debug().
- logging.error(msg[, *args[, **kwargs]])¶
- Записывает сообщение с уровнем ERROR в корневой регистратор. Аргументы интерпретируются так же, как для debug().
- logging.critical(msg[, *args[, **kwargs]])¶
- Регистрирует сообщение с уровнем CRITICAL в корневом логгере. Аргументы интерпретируются так же, как для debug().
- logging.exception(msg[, *args])¶
- Регистрирует сообщение с уровнем ERROR в корневом логгере. Аргументы интерпретируются так же, как для debug(). Информация об исключении добавляется в сообщение журнала. Эта функция должна вызываться только из обработчика исключений.
- logging.log(level, msg[, *args[, **kwargs]])¶
- Регистрирует сообщение с уровнем level в корневом логгере. Остальные аргументы интерпретируются так же, как для debug().
- logging.disable(lvl)¶
- Задаёт переопределяющий уровень lvl для всех логгеров, который имеет приоритет над собственным уровнем логгера. Когда возникает необходимость временно снизить объём вывода логирования во всём приложении, эта функция может быть полезна.
- logging.addLevelName(lvl, levelName)¶
- Связывает уровень lvl с текстом levelName во внутреннем словаре, который используется для сопоставления числовых уровней с текстовым представлением, например, когда Formatter форматирует сообщение. Эта функция также может использоваться для определения собственных уровней. Единственные ограничения: все используемые уровни должны быть зарегистрированы с помощью этой функции, уровни должны быть положительными целыми числами и должны возрастать в порядке увеличения серьёзности.
- logging.getLevelName(lvl)¶
- Возвращает текстовое представление уровня логирования lvl. Если уровень является одним из предопределённых уровней CRITICAL, ERROR, WARNING, INFO или DEBUG, то возвращается соответствующая строка. Если вы связали уровни с именами с помощью addLevelName(), то возвращается имя, которое вы связали с lvl. Если передаётся числовое значение, соответствующее одному из определённых уровней, возвращается соответствующее строковое представление. В противном случае возвращается строка «Level %s» % lvl.
- logging.makeLogRecord(attrdict)¶
- Создаёт и возвращает новый экземпляр LogRecord, атрибуты которого определяются attrdict. Эта функция полезна для приёма сериализованного (pickled) LogRecord словаря атрибутов, отправленного через сокет, и восстановления его как экземпляра LogRecord на принимающей стороне.
- logging.basicConfig([**kwargs])¶
Выполняет базовую настройку системы логирования, создавая StreamHandler со стандартным Formatter и добавляя его в корневой логгер. Функция ничего не делает, если для корневого логгера уже определены какие-либо обработчики. Функции debug(), info(), warning(), error() и critical() автоматически вызывают basicConfig(), если для корневого логгера не определены обработчики.
Поддерживаются следующие аргументы ключевых слов.
Формат Описание filename Указывает, что создаётся FileHandler, с использованием указанного имени файла, а не StreamHandler. filemode Задает режим открытия файла, если указан filename (если filemode не указан, по умолчанию используется ‘a’). format Использует указанную строку формата для обработчика. datefmt Использовать указанный формат даты/времени. level Установить уровень корневого регистратора на указанный уровень. поток данных Использовать указанный поток данных для инициализации StreamHandler. Обратите внимание, что этот аргумент несовместим с «filename» – если оба присутствуют, «поток данных» игнорируется.
- logging.shutdown()¶
- Сообщает системе логирования о необходимости выполнить упорядоченное завершение работы, сбрасывая и закрывая все обработчики. Эту функцию следует вызывать при завершении приложения, и после этого вызова не следует использовать систему логирования.
- logging.setLoggerClass(klass)¶
- Указывает системе логирования использовать класс klass при создании экземпляра логгера. Класс должен определять __init__() таким образом, чтобы требовался только аргумент name, а __init__() должен вызывать Logger.__init__(). Эта функция обычно вызывается до создания любых логгеров приложениями, которым требуется использовать собственное поведение логгера.
См. также
- PEP 282 – Система логирования
- Предложение, описывающее эту возможность для включения в стандартную библиотеку Python.
- Оригинальный пакет логирования Python
- Это исходный код пакета logging. Версия, доступная на этом сайте, подходит для Python 1.5.2, 2.1.x и 2.2.x, в которых пакет logging не входит в стандартную библиотеку.
Объекты Logger¶Logger Objects
Логгеры имеют следующие атрибуты и методы. Обратите внимание, что логгеры никогда не создаются напрямую, а всегда через функцию уровня модуля logging.getLogger(name).
- Logger.propagate¶
- Если это значение равно false, то сообщения логирования не передаются этим логгером или дочерними логгерами логгерам более высокого уровня (предкам). Конструктор устанавливает этот атрибут в 1.
- Logger.setLevel(lvl)¶
Устанавливает порог для этого логгера в lvl. Сообщения логирования, уровень которых ниже lvl, будут игнорироваться. При создании логгера уровень устанавливается в NOTSET (что приводит к обработке всех сообщений, если логгер является корневым, или к делегированию родительскому логгеру, если логгер не корневой). Обратите внимание, что корневой логгер создаётся с уровнем WARNING.
Термин «делегирование родителю» означает, что если логгер имеет уровень NOTSET, то просматривается цепочка его логгеров-предков, пока не будет найден предок с уровнем, отличным от NOTSET, или не будет достигнут корневой логгер.
Если найден предок с уровнем, отличным от NOTSET, то его уровень считается действующим уровнем регистратора, с которого начался поиск предка, и используется для определения обработки события логирования.
Если достигнут корень и его уровень NOTSET, то будут обработаны все сообщения. В противном случае уровень корня будет использоваться как действующий уровень.
- Logger.isEnabledFor(lvl)¶
- Указывает, будет ли сообщение с уровнем серьёзности lvl обработано этим логгером. Этот метод сначала проверяет уровень, установленный на уровне модуля с помощью logging.disable(lvl), а затем эффективный уровень логгера, определяемый getEffectiveLevel().
- Logger.getEffectiveLevel()¶
- Указывает эффективный уровень для данного логгера. Если с помощью setLevel() было установлено значение, отличное от NOTSET, возвращается оно. В противном случае выполняется обход иерархии к корню, пока не будет найдено значение, отличное от NOTSET, и возвращается это значение.
- Logger.debug(msg[, *args[, **kwargs]])¶
Записывает сообщение уровня DEBUG в этот логгер. msg – это строка форматирования сообщения, а args – аргументы, которые подставляются в msg с помощью оператора форматирования строк. (Обратите внимание, что это означает, что можно использовать ключевые слова в строке форматирования вместе с одним словарным аргументом.)
В kwargs проверяются два именованных аргумента: exc_info, который, если он не равен false, приводит к добавлению информации об исключении в сообщение логирования. Если предоставлен кортеж исключения (в формате, возвращаемом sys.exc_info()), он используется; в противном случае вызывается sys.exc_info() для получения информации об исключении.
Другим необязательным именованным аргументом является extra, который можно использовать для передачи словаря, которым заполняется __dict__ объекта LogRecord, созданного для события логирования, пользовательскими атрибутами. Затем эти пользовательские атрибуты можно использовать по своему усмотрению. Например, их можно включать в логируемые сообщения. Например:
FORMAT = "%(asctime)-15s %(clientip)s %(user)-8s %(message)s" logging.basicConfig(format=FORMAT) d = { 'clientip' : '192.168.0.1', 'user' : 'fbloggs' } logger = logging.getLogger("tcpserver") logger.warning("Protocol problem: %s", "connection reset", extra=d)
выведет что-то вроде
2006-02-08 22:20:02,165 192.168.0.1 fbloggs Protocol problem: connection reset
Ключи в словаре, переданном в extra, не должны пересекаться с ключами, используемыми системой логирования. (См. документацию Formatter для получения дополнительной информации о том, какие ключи используются системой логирования.)
Если вы решите использовать эти атрибуты в логируемых сообщениях, нужно соблюдать осторожность. Например, в приведённом выше примере Formatter настроен с форматной строкой, которая ожидает 'clientip' и 'user' в словаре атрибутов LogRecord. Если они отсутствуют, сообщение не будет записано, так как возникнет исключение форматирования строки. Поэтому в этом случае всегда нужно передавать словарь extra с этими ключами.
Хотя это может быть раздражающим, эта функция предназначена для использования в специализированных обстоятельствах, таких как многопоточные серверы, где один и тот же код выполняется во многих контекстах, и интересующие условия зависят от этого контекста (например, IP-адрес удалённого клиента и имя аутентифицированного пользователя в приведённом выше примере). В таких обстоятельствах, скорее всего, будут использоваться специализированные Formatter с конкретными Handler.
- Logger.info(msg[, *args[, **kwargs]])¶
- Записывает сообщение уровня INFO в этот логгер. Аргументы интерпретируются так же, как для debug().
- Logger.warning(msg[, *args[, **kwargs]])¶
- Записывает сообщение уровня WARNING в этот логгер. Аргументы интерпретируются так же, как для debug().
- Logger.error(msg[, *args[, **kwargs]])¶
- Записывает сообщение уровня ERROR в этот логгер. Аргументы интерпретируются так же, как для debug().
- Logger.critical(msg[, *args[, **kwargs]])¶
- Записывает сообщение с уровнем CRITICAL в этот логгер. Аргументы интерпретируются так же, как для debug().
- Logger.log(lvl, msg[, *args[, **kwargs]])¶
- Записывает сообщение с целочисленным уровнем lvl в этот логгер. Остальные аргументы интерпретируются так же, как для debug().
- Logger.exception(msg[, *args])¶
- Записывает сообщение с уровнем ERROR в этот логгер. Аргументы интерпретируются так же, как для debug(). Информация об исключении добавляется в сообщение лога. Этот метод следует вызывать только из обработчика исключений.
- Logger.addFilter(filt)¶
- Добавляет указанный фильтр filt к этому логгеру.
- Logger.removeFilter(filt)¶
- Удаляет указанный фильтр filt из этого логгера.
- Logger.filter(record)¶
- Применяет фильтры этого регистратора к записи и возвращает истинное значение, если запись должна быть обработана.
- Logger.addHandler(hdlr)¶
- Добавляет указанный обработчик hdlr к этому логгеру.
- Logger.removeHandler(hdlr)¶
- Удаляет указанный обработчик hdlr из этого логгера.
- Logger.findCaller()¶
- Находит имя исходного файла и номер строки вызывающего кода. Возвращает имя файла, номер строки и имя функции в виде кортежа из трёх элементов.
- Logger.handle(record)¶
- Обрабатывает запись, передавая её всем обработчикам, связанным с этим логгером и его предками (пока не будет найдено ложное значение propagate). Этот метод используется для распакованных записей, полученных из сокета, а также для записей, созданных локально. Фильтрация на уровне логгера применяется с помощью filter().
Простой пример¶Basic example
Пакет logging предоставляет большую гибкость, и его настройка может показаться пугающей. В этом разделе показано, что простое использование пакета logging вполне возможно.
Самый простой пример показывает вывод сообщений в консоль:
import logging
logging.debug('A debug message')
logging.info('Some information')
logging.warning('A shot across the bows')
Если запустить приведённый выше скрипт, результат будет таким:
WARNING:root:A shot across the bows
Поскольку не был указан конкретный регистратор, система использовала корневой регистратор. Сообщения уровней debug и info не появились, потому что по умолчанию корневой регистратор настроен на обработку только сообщений с уровнем WARNING и выше. Формат сообщений также задаётся по умолчанию, как и место вывода сообщений – sys.stderr. Уровень, формат и место вывода можно легко изменить, как показано в примере ниже:
import logging
logging.basicConfig(level=logging.DEBUG,
format='%(asctime)s %(levelname)s %(message)s',
filename='/tmp/myapp.log',
filemode='w')
logging.debug('A debug message')
logging.info('Some information')
logging.warning('A shot across the bows')
Метод basicConfig() используется для изменения настроек конфигурации по умолчанию, в результате чего вывод (записываемый в /tmp/myapp.log) должен выглядеть примерно следующим образом:
2004-07-02 13:00:08,743 DEBUG A debug message
2004-07-02 13:00:08,743 INFO Some information
2004-07-02 13:00:08,743 WARNING A shot across the bows
На этот раз были обработаны все сообщения с уровнем DEBUG и выше, был изменён формат сообщений, а вывод пошёл в указанный файл, а не в консоль.
Форматирование использует старый стиль форматирования строк Python – см. раздел Старые операции форматирования строк. Строка формата принимает следующие общие спецификаторы. Полный список спецификаторов можно найти в документации Formatter.
| Формат | Описание |
|---|---|
| %(name)s | Имя логгера (канала журналирования). |
| %(levelname)s | Текстовый уровень логирования сообщения ('DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'). |
| %(asctime)s | Человекочитаемое время создания LogRecord. По умолчанию оно имеет вид “2003-07-08 16:49:45,896” (числа после запятой – миллисекунды времени). |
| %(message)s | Записанное сообщение. |
Чтобы изменить формат даты/времени, можно передать дополнительный именованный параметр datefmt, как показано ниже:
import logging
logging.basicConfig(level=logging.DEBUG,
format='%(asctime)s %(levelname)-8s %(message)s',
datefmt='%a, %d %b %Y %H:%M:%S',
filename='/temp/myapp.log',
filemode='w')
logging.debug('A debug message')
logging.info('Some information')
logging.warning('A shot across the bows')
в результате чего вывод будет выглядеть как
Fri, 02 Jul 2004 13:06:18 DEBUG A debug message
Fri, 02 Jul 2004 13:06:18 INFO Some information
Fri, 02 Jul 2004 13:06:18 WARNING A shot across the bows
Строка формата даты соответствует требованиям strftime() – см. документацию модуля time.
Если вместо отправки вывода журнала в консоль или файл вы хотите использовать объект, подобный файлу, созданный отдельно, вы можете передать его в basicConfig() с помощью именованного аргумента stream. Обратите внимание, что если переданы оба именованных аргумента stream и filename, аргумент stream игнорируется.
Конечно, вы можете включать переменную информацию в вывод. Для этого просто сделайте сообщение строкой формата и передайте дополнительные аргументы, содержащие переменную информацию, как в следующем примере:
import logging
logging.basicConfig(level=logging.DEBUG,
format='%(asctime)s %(levelname)-8s %(message)s',
datefmt='%a, %d %b %Y %H:%M:%S',
filename='/temp/myapp.log',
filemode='w')
logging.error('Pack my box with %d dozen %s', 5, 'liquor jugs')
в результате чего получится
Wed, 21 Jul 2004 15:35:16 ERROR Pack my box with 5 dozen liquor jugs
Журналирование в несколько мест назначения¶Logging to multiple destinations
Допустим, вы хотите вести журнал в консоль и файл с разными форматами сообщений и в разных ситуациях. Предположим, вы хотите записывать сообщения уровня DEBUG и выше в файл, а сообщения уровня INFO и выше – в консоль. Также предположим, что файл должен содержать временные метки, а консольные сообщения – нет. Вот как это можно сделать:
import logging
# настроить логирование в файл – подробнее см. предыдущий раздел
logging.basicConfig(level=logging.DEBUG,
format='%(asctime)s %(name)-12s %(levelname)-8s %(message)s',
datefmt='%m-%d %H:%M',
filename='/temp/myapp.log',
filemode='w')
# Определяет обработчик, который записывает сообщения уровня INFO и выше в sys.stderr.
console = logging.StreamHandler()
console.setLevel(logging.INFO)
# Задает формат, упрощенный для вывода в консоль.
formatter = logging.Formatter('%(name)-12s: %(levelname)-8s %(message)s')
# Указывает обработчику использовать этот формат.
console.setFormatter(formatter)
# Добавляет обработчик в корневой логгер.
logging.getLogger('').addHandler(console)
# Теперь можно выполнять запись в корневой логгер или любой другой. Сначала корневой...
logging.info('Jackdaws love my big sphinx of quartz.')
# Теперь определите несколько других логгеров, которые могут представлять разные части приложения:
# приложение:
logger1 = logging.getLogger('myapp.area1')
logger2 = logging.getLogger('myapp.area2')
logger1.debug('Quick zephyrs blow, vexing daft Jim.')
logger1.info('How quickly daft jumping zebras vex.')
logger2.warning('Jail zesty vixen who grabbed pay from quack.')
logger2.error('The five boxing wizards jump quickly.')
При запуске на консоли вы увидите
root : INFO Jackdaws love my big sphinx of quartz.
myapp.area1 : INFO How quickly daft jumping zebras vex.
myapp.area2 : WARNING Jail zesty vixen who grabbed pay from quack.
myapp.area2 : ERROR The five boxing wizards jump quickly.
а в файле увидите примерно такое
10-22 22:19 root INFO Jackdaws love my big sphinx of quartz.
10-22 22:19 myapp.area1 DEBUG Quick zephyrs blow, vexing daft Jim.
10-22 22:19 myapp.area1 INFO How quickly daft jumping zebras vex.
10-22 22:19 myapp.area2 WARNING Jail zesty vixen who grabbed pay from quack.
10-22 22:19 myapp.area2 ERROR The five boxing wizards jump quickly.
Как видите, сообщение DEBUG отображается только в файле. Остальные сообщения отправляются в оба места назначения.
В этом примере используются консольный и файловый обработчики, но вы можете использовать любое количество и любое сочетание обработчиков по своему выбору.
Добавление контекстной информации в вывод журнала¶Adding contextual information to your logging output
Иногда требуется, чтобы вывод логирования содержал контекстную информацию в дополнение к параметрам, переданным в вызов логирования. Например, в сетевом приложении может быть желательно регистрировать специфичную для клиента информацию в журнале (например, имя пользователя удаленного клиента или IP-адрес). Хотя вы можете использовать параметр extra для этого, не всегда удобно передавать информацию таким образом. Хотя может возникнуть соблазн создавать экземпляры Logger для каждого соединения, это не очень хорошая идея, потому что эти экземпляры не собираются сборщиком мусора. Хотя на практике это не проблема, когда количество экземпляров Logger зависит от уровня детализации, которую вы хотите использовать при логировании приложения, это может быть трудно управляемым, если количество экземпляров Logger становится фактически неограниченным.
Простой способ передавать контекстную информацию для вывода вместе с событием логирования – использовать класс LoggerAdapter. Этот класс устроен так, что выглядит как Logger, поэтому можно вызывать debug(), info(), warning(), error(), exception(), critical() и log(). Сигнатуры этих методов совпадают с их аналогами в Logger, так что экземпляры обоих типов можно использовать взаимозаменяемо.
При создании экземпляра LoggerAdapter ему передаются экземпляр Logger и объект, подобный словарю, содержащий контекстную информацию. При вызове любого из методов логирования у экземпляра LoggerAdapter он делегирует вызов нижележащему экземпляру Logger, переданному в его конструктор, и обеспечивает передачу контекстной информации в делегированном вызове. Вот фрагмент кода LoggerAdapter:
def debug(self, msg, *args, **kwargs):
"""
Передаёт вызов отладки нижележащему регистратору после добавления
контекстной информации из данного экземпляра адаптера.
"""
msg, kwargs = self.process(msg, kwargs)
self.logger.debug(msg, *args, **kwargs)
Метод process() класса LoggerAdapter – это место, где контекстная информация добавляется в вывод логирования. Ему передаются сообщение и именованные аргументы вызова логирования, и он возвращает (возможно) изменённые версии этих данных для использования в вызове нижележащего логгера. В реализации по умолчанию этот метод оставляет сообщение без изменений, но добавляет ключ «extra» в именованные аргументы, значением которого является объект, подобный словарю, переданный в конструктор. Разумеется, если в вызове адаптера уже был передан именованный аргумент «extra», он будет молча перезаписан.
Преимущество использования «extra» в том, что значения из объекта, подобного словарю, сливаются в LogRecord.__dict__, что позволяет использовать настроенные строки с экземплярами Formatter, которым известны ключи этого объекта. Если нужен другой подход, например, требуется добавить контекстную информацию в начало или конец строки сообщения, достаточно создать подкласс LoggerAdapter и переопределить process(), чтобы он делал то, что нужно. Вот пример скрипта, использующего этот класс, который также демонстрирует, какое поведение, подобное словарю, требуется от произвольного «dict-like» объекта для использования в конструкторе:
import logging
class ConnInfo:
"""
An example class which shows how an arbitrary class can be used as
the 'extra' context information repository passed to a LoggerAdapter.
"""
def __getitem__(self, name):
"""
To allow this instance to look like a dict.
"""
from random import choice
if name == "ip":
result = choice(["127.0.0.1", "192.168.0.1"])
elif name == "user":
result = choice(["jim", "fred", "sheila"])
else:
result = self.__dict__.get(name, "?")
return result
def __iter__(self):
"""
To allow iteration over keys, which will be merged into
the LogRecord dict before formatting and output.
"""
keys = ["ip", "user"]
keys.extend(self.__dict__.keys())
return keys.__iter__()
if __name__ == "__main__":
from random import choice
levels = (logging.DEBUG, logging.INFO, logging.WARNING, logging.ERROR, logging.CRITICAL)
a1 = logging.LoggerAdapter(logging.getLogger("a.b.c"),
{ "ip" : "123.231.231.123", "user" : "sheila" })
logging.basicConfig(level=logging.DEBUG,
format="%(asctime)-15s %(name)-5s %(levelname)-8s IP: %(ip)-15s User: %(user)-8s %(message)s")
a1.debug("A debug message")
a1.info("An info message with %s", "some parameters")
a2 = logging.LoggerAdapter(logging.getLogger("d.e.f"), ConnInfo())
for x in range(10):
lvl = choice(levels)
lvlname = logging.getLevelName(lvl)
a2.log(lvl, "A message at %s level with %d %s", lvlname, 2, "parameters")
При запуске этого скрипта вывод должен выглядеть примерно так:
2008-01-18 14:49:54,023 a.b.c DEBUG IP: 123.231.231.123 User: sheila A debug message
2008-01-18 14:49:54,023 a.b.c INFO IP: 123.231.231.123 User: sheila An info message with some parameters
2008-01-18 14:49:54,023 d.e.f CRITICAL IP: 192.168.0.1 User: jim A message at CRITICAL level with 2 parameters
2008-01-18 14:49:54,033 d.e.f INFO IP: 192.168.0.1 User: jim A message at INFO level with 2 parameters
2008-01-18 14:49:54,033 d.e.f WARNING IP: 192.168.0.1 User: sheila A message at WARNING level with 2 parameters
2008-01-18 14:49:54,033 d.e.f ERROR IP: 127.0.0.1 User: fred A message at ERROR level with 2 parameters
2008-01-18 14:49:54,033 d.e.f ERROR IP: 127.0.0.1 User: sheila A message at ERROR level with 2 parameters
2008-01-18 14:49:54,033 d.e.f WARNING IP: 192.168.0.1 User: sheila A message at WARNING level with 2 parameters
2008-01-18 14:49:54,033 d.e.f WARNING IP: 192.168.0.1 User: jim A message at WARNING level with 2 parameters
2008-01-18 14:49:54,033 d.e.f INFO IP: 192.168.0.1 User: fred A message at INFO level with 2 parameters
2008-01-18 14:49:54,033 d.e.f WARNING IP: 192.168.0.1 User: sheila A message at WARNING level with 2 parameters
2008-01-18 14:49:54,033 d.e.f WARNING IP: 127.0.0.1 User: jim A message at WARNING level with 2 parameters
Отправка и получение событий логирования по сети¶Sending and receiving logging events across a network
Допустим, требуется отправлять события логирования по сети и обрабатывать их на принимающей стороне. Простой способ – прикрепить экземпляр SocketHandler к корневому логгеру на отправляющей стороне:
import logging, logging.handlers
rootLogger = logging.getLogger('')
rootLogger.setLevel(logging.DEBUG)
socketHandler = logging.handlers.SocketHandler('localhost',
logging.handlers.DEFAULT_TCP_LOGGING_PORT)
# Не нужно использовать форматтер, так как сокет-обработчик отправляет событие в виде неформатированного пикла.
# неформатированный pickle
rootLogger.addHandler(socketHandler)
# Теперь можно выполнять запись в корневой логгер или любой другой. Сначала корневой...
logging.info('Jackdaws love my big sphinx of quartz.')
# Теперь определите несколько других логгеров, которые могут представлять разные части приложения:
# приложение:
logger1 = logging.getLogger('myapp.area1')
logger2 = logging.getLogger('myapp.area2')
logger1.debug('Quick zephyrs blow, vexing daft Jim.')
logger1.info('How quickly daft jumping zebras vex.')
logger2.warning('Jail zesty vixen who grabbed pay from quack.')
logger2.error('The five boxing wizards jump quickly.')
На принимающей стороне можно настроить приемник с помощью модуля socketserver. Вот базовый рабочий пример:
import cPickle
import logging
import logging.handlers
import socketserver
import struct
class LogRecordStreamHandler(socketserver.StreamRequestHandler):
"""Handler for a streaming logging request.
This basically logs the record using whatever logging policy is
configured locally.
"""
def handle(self):
"""
Handle multiple requests - each expected to be a 4-byte length,
followed by the LogRecord in pickle format. Logs the record
according to whatever policy is configured locally.
"""
while True:
chunk = self.connection.recv(4)
if len(chunk) < 4:
break
slen = struct.unpack(">L", chunk)[0]
chunk = self.connection.recv(slen)
while len(chunk) < slen:
chunk = chunk + self.connection.recv(slen - len(chunk))
obj = self.unPickle(chunk)
record = logging.makeLogRecord(obj)
self.handleLogRecord(record)
def unPickle(self, data):
return cPickle.loads(data)
def handleLogRecord(self, record):
# if a name is specified, we use the named logger rather than the one
# implied by the record.
if self.server.logname is not None:
name = self.server.logname
else:
name = record.name
logger = logging.getLogger(name)
# N.B. EVERY record gets logged. This is because Logger.handle
# is normally called AFTER logger-level filtering. If you want
# to do filtering, do it at the client end to save wasting
# cycles and network bandwidth!
logger.handle(record)
class LogRecordSocketReceiver(socketserver.ThreadingTCPServer):
"""simple TCP socket-based logging receiver suitable for testing.
"""
allow_reuse_address = 1
def __init__(self, host='localhost',
port=logging.handlers.DEFAULT_TCP_LOGGING_PORT,
handler=LogRecordStreamHandler):
socketserver.ThreadingTCPServer.__init__(self, (host, port), handler)
self.abort = 0
self.timeout = 1
self.logname = None
def serve_until_stopped(self):
import select
abort = 0
while not abort:
rd, wr, ex = select.select([self.socket.fileno()],
[], [],
self.timeout)
if rd:
self.handle_request()
abort = self.abort
def main():
logging.basicConfig(
format="%(relativeCreated)5d %(name)-15s %(levelname)-8s %(message)s")
tcpserver = LogRecordSocketReceiver()
print("About to start TCP server...")
tcpserver.serve_until_stopped()
if __name__ == "__main__":
main()
Сначала запустите сервер, затем клиент. На стороне клиента в консоль ничего не выводится; на стороне сервера вы должны увидеть что-то вроде:
About to start TCP server...
59 root INFO Jackdaws love my big sphinx of quartz.
59 myapp.area1 DEBUG Quick zephyrs blow, vexing daft Jim.
69 myapp.area1 INFO How quickly daft jumping zebras vex.
69 myapp.area2 WARNING Jail zesty vixen who grabbed pay from quack.
69 myapp.area2 ERROR The five boxing wizards jump quickly.
Объекты-обработчики¶Handler Objects
Обработчики имеют следующие атрибуты и методы. Обратите внимание, что Handler никогда не создаётся напрямую; этот класс служит базой для более полезных подклассов. Однако метод __init__() в подклассах должен вызывать Handler.__init__().
- Handler.__init__(level=NOTSET)¶
- Инициализирует экземпляр Handler, устанавливая его уровень, устанавливая список фильтров в пустой список и создавая блокировку (с помощью createLock()) для сериализации доступа к механизму ввода-вывода.
- Handler.createLock()¶
- Инициализирует блокировку потока, которую можно использовать для сериализации доступа к нижележащей функциональности ввода-вывода, которая может не быть потокобезопасной.
- Handler.acquire()¶
- Захватывает блокировку потока, созданную с помощью createLock().
- Handler.setLevel(lvl)¶
- Устанавливает порог для этого обработчика в lvl. Сообщения журнала, которые менее серьезны, чем lvl, будут игнорироваться. При создании обработчика уровень устанавливается в NOTSET (из-за чего обрабатываются все сообщения).
- Handler.addFilter(filt)¶
- Добавляет указанный фильтр фильтр в данный обработчик.
- Handler.removeFilter(filt)¶
- Удаляет указанный фильтр фильтр из данного обработчика.
- Handler.filter(record)¶
- Применяет фильтры этого обработчика к записи и возвращает true, если запись должна быть обработана.
- Handler.flush()¶
- Обеспечивает сброс всего вывода логирования. Данная версия ничего не делает и предназначена для реализации в подклассах.
- Handler.close()¶
- Освобождает все ресурсы, используемые обработчиком. Эта версия не производит вывод, но удаляет обработчик из внутреннего списка обработчиков, который закрывается при вызове shutdown(). Подклассы должны гарантировать, что этот метод вызывается из переопределенных методов close().
- Handler.handle(record)¶
- Условно выдает указанную запись логирования в зависимости от фильтров, которые могут быть добавлены к обработчику. Оборачивает фактическую выдачу записи в захват/освобождение блокировки потока ввода-вывода.
- Handler.handleError(record)¶
- Этот метод следует вызывать из обработчиков при возникновении исключения во время вызова emit(). По умолчанию он ничего не делает, то есть исключения молча игнорируются. В системе логирования это в основном то, что нужно – большинство пользователей не интересуются ошибками в системе логирования, их больше волнуют ошибки приложения. Однако при желании можно заменить этот метод собственным обработчиком. Указанная запись (record) – это та, которая обрабатывалась в момент возникновения исключения.
- Handler.format(record)¶
- Форматирует запись – если задан форматировщик, использует его. В противном случае использует форматировщик по умолчанию для модуля.
- Handler.emit(record)¶
- Делает все необходимое для фактической записи указанной записи журнала. Эта версия предназначена для реализации в подклассах и поэтому вызывает NotImplementedError.
StreamHandler¶
Класс StreamHandler, находящийся в основном пакете logging, направляет вывод логирования в потоки, такие как sys.stdout, sys.stderr или любой объект, похожий на файл (точнее, любой объект, поддерживающий методы write() и flush()).
- class logging.StreamHandler([strm])¶
Возвращает новый экземпляр класса StreamHandler. Если указан strm, экземпляр будет использовать его для вывода журнала; в противном случае будет использоваться sys.stderr.
- emit(record)¶
- Если задан форматтер, он используется для форматирования записи. Затем запись записывается в поток с завершающим символом новой строки. Если присутствует информация об исключении, она форматируется с помощью traceback.print_exception() и добавляется в поток.
FileHandler¶
Класс FileHandler, находящийся в основном пакете logging, направляет вывод логирования в файл на диске. Он наследует функциональность вывода от StreamHandler.
- class logging.FileHandler(filename[, mode[, encoding[, delay]]])¶
Возвращает новый экземпляр класса FileHandler. Указанный файл открывается и используется как поток для логирования. Если mode не указан, используется 'a'. Если encoding не равен None, файл открывается с указанной кодировкой. Если delay равен true, открытие файла откладывается до первого вызова emit(). По умолчанию файл растёт бесконечно.
- close()¶
- Закрывает файл.
- emit(record)¶
- Выводит запись в файл.
NullHandler¶
Новое в версии 3.1.
Класс NullHandler, находящийся в основном пакете logging, не выполняет никакого форматирования или вывода. По сути, это обработчик-пустышка, предназначенный для разработчиков библиотек.
- class logging.NullHandler¶
Возвращает новый экземпляр класса NullHandler.
- emit(record)¶
- Этот метод ничего не делает.
Смотрите Настройка логирования для библиотеки для получения дополнительной информации об использовании NullHandler.
WatchedFileHandler¶
Класс WatchedFileHandler, находящийся в модуле logging.handlers, представляет собой FileHandler, который следит за файлом, в который ведётся логирование. Если файл изменяется, он закрывается и открывается заново по тому же имени файла.
Файл может измениться из-за использования таких программ, как newsyslog и logrotate, которые выполняют ротацию файлов журнала. Этот обработчик, предназначенный для использования в Unix/Linux, следит, не изменился ли файл с момента последнего вызова emit. (Считается, что файл изменился, если изменились его устройство или inode.) Если файл изменился, старый поток файла закрывается, а файл открывается для получения нового потока.
Этот обработчик не подходит для использования в Windows, поскольку в Windows открытые файлы журналов нельзя перемещать или переименовывать – система логирования открывает файлы с эксклюзивными блокировками, – поэтому в таком обработчике нет необходимости. Кроме того, ST_INO не поддерживается в Windows; stat() всегда возвращает ноль для этого значения.
- class logging.handlers.WatchedFileHandler(filename[, mode[, encoding[, delay]]])¶
Возвращает новый экземпляр класса WatchedFileHandler. Указанный файл открывается и используется как поток для логирования. Если mode не указан, используется 'a'. Если encoding не равен None, файл открывается с указанной кодировкой. Если delay равен true, открытие файла откладывается до первого вызова emit(). По умолчанию файл растёт бесконечно.
- emit(record)¶
- Выводит запись в файл, но сначала проверяет, изменился ли файл. Если изменился, существующий поток сбрасывается и закрывается, а файл открывается заново, после чего запись выводится в файл.
RotatingFileHandler¶
Класс RotatingFileHandler, находящийся в модуле logging.handlers, поддерживает ротацию файлов журналов на диске.
- class logging.handlers.RotatingFileHandler(filename[, mode[, maxBytes[, backupCount[, encoding[, delay]]]]])¶
Возвращает новый экземпляр класса RotatingFileHandler. Указанный файл открывается и используется как поток для журналирования. Если mode не указан, используется 'a'. Если encoding не равен None, файл открывается с указанной кодировкой. Если delay равен истине, открытие файла откладывается до первого вызова emit(). По умолчанию файл растёт бесконечно.
С помощью параметров maxBytes и backupCount можно настроить ротацию файла при достижении заданного размера. Когда размер собирается быть превышен, файл закрывается и новый файл молча открывается для записи. Ротация происходит всякий раз, когда текущий файл журнала достигает длины, близкой к maxBytes; если maxBytes равен нулю, ротация не происходит. Если backupCount не равен нулю, система сохраняет старые файлы журналов, добавляя к имени файла расширения «.1», «.2» и т.д. Например, при backupCount равном 5 и исходном имени файла app.log вы получите app.log, app.log.1, app.log.2, вплоть до app.log.5. Файл, в который ведется запись, всегда app.log. Когда этот файл заполняется, он закрывается и переименовывается в app.log.1, а если существуют файлы app.log.1, app.log.2 и т.д., то они переименовываются соответственно в app.log.2, app.log.3 и так далее.
- doRollover()¶
- Выполняет ротацию, как описано выше.
- emit(record)¶
- Записывает запись (record) в файл, выполняя при необходимости ротацию, как описано ранее.
TimedRotatingFileHandler¶
Класс TimedRotatingFileHandler, находящийся в модуле logging.handlers, поддерживает ротацию файлов журналов на диске через заданные промежутки времени.
- class logging.handlers.TimedRotatingFileHandler(filename[, when[, interval[, backupCount[, encoding[, delay[, utc]]]]]])¶
Возвращает новый экземпляр класса TimedRotatingFileHandler. Указанный файл открывается и используется как поток для журналирования. При ротации также устанавливается суффикс имени файла. Ротация происходит на основе произведения when и interval.
Параметр when указывает тип interval. Список возможных значений приведён ниже. Обратите внимание, что регистр не учитывается.
Значение Тип интервала 'S' Секунды 'M' Минуты 'H' Часы 'D' Дни 'W' День недели (0 = понедельник) 'midnight' Переключение в полночь Система будет сохранять старые файлы журнала, добавляя расширения к имени файла. Расширения основаны на дате и времени, с использованием формата strftime %Y-%m-%d_%H-%M-%S или его начальной части, в зависимости от интервала смены файлов. Если аргумент utc равен истине, будут использоваться времена в UTC; в противном случае используется местное время.
Если backupCount не равен нулю, сохраняется не более backupCount файлов; если при ротации должно быть создано больше файлов, самый старый удаляется. Логика удаления использует интервал для определения того, какие файлы нужно удалить, поэтому изменение интервала может привести к тому, что старые файлы останутся.
- doRollover()¶
- Выполняет ротацию, как описано выше.
- emit(record)¶
- Выводит запись в файл, обеспечивая ротацию, как описано выше.
SocketHandler¶
Класс SocketHandler, расположенный в модуле logging.handlers, отправляет вывод журнала на сетевой сокет. Базовый класс использует TCP-сокет.
- class logging.handlers.SocketHandler(host, port)¶
Возвращает новый экземпляр класса SocketHandler, предназначенного для взаимодействия с удаленной машиной, адрес которой задается host и port.
- close()¶
- Закрывает сокет.
- emit()¶
- Сериализует словарь атрибутов записи и записывает его в сокет в бинарном формате. Если при работе с сокетом возникает ошибка, пакет молча отбрасывается. Если соединение было ранее потеряно, оно восстанавливается. Для десериализации записи на принимающей стороне в LogRecord используйте функцию makeLogRecord().
- handleError()¶
- Обрабатывает ошибку, возникшую во время emit(). Наиболее вероятная причина – потеря соединения. Закрывает сокет, чтобы можно было повторить попытку при следующем событии.
- makeSocket()¶
- Это фабричный метод, позволяющий подклассам определять точный тип сокета, который они хотят. Реализация по умолчанию создает TCP-сокет (socket.SOCK_STREAM).
- makePickle(record)¶
- Сериализует словарь атрибутов записи в двоичном формате с префиксом длины и возвращает его готовым к передаче через сокет.
- send(packet)¶
- Отправляет сериализованную с помощью pickle строку packet в сокет. Эта функция допускает частичную отправку, которая может произойти, когда сеть занята.
DatagramHandler¶
Класс DatagramHandler, расположенный в модуле logging.handlers , наследуется от SocketHandler для поддержки отправки сообщений журнала через UDP-сокеты.
- class logging.handlers.DatagramHandler(host, port)¶
Возвращает новый экземпляр класса DatagramHandler, предназначенного для взаимодействия с удаленной машиной, адрес которой задается host и port.
- emit()¶
- Сериализует словарь атрибутов записи и записывает его в сокет в бинарном формате. Если при работе с сокетом возникает ошибка, пакет молча отбрасывается. Для десериализации записи на принимающей стороне в LogRecord используйте функцию makeLogRecord().
- makeSocket()¶
- Фабричный метод SocketHandler здесь переопределен для создания UDP-сокета (socket.SOCK_DGRAM).
- send(s)¶
- Отправляет сериализованную с помощью pickle строку в сокет.
SysLogHandler¶
Класс SysLogHandler, расположенный в модуле logging.handlers, поддерживает отправку сообщений журнала в удаленный или локальный системный журнал Unix.
- class logging.handlers.SysLogHandler([address[, facility]])¶
Возвращает новый экземпляр класса SysLogHandler, предназначенный для связи с удаленной Unix-машиной, адрес которой задается параметром address в виде кортежа (host, port). Если address не указан, используется ('localhost', 514). Адрес используется для открытия UDP-сокета. Альтернативой кортежу (host, port) является указание адреса в виде строки, например “/dev/log”. В этом случае для отправки сообщения в syslog используется сокет домена Unix. Если facility не указан, используется LOG_USER.
- close()¶
- Закрывает сокет, подключённый к удалённому узлу.
- emit(record)¶
- Запись форматируется и отправляется на сервер syslog. Если присутствует информация об исключении, она не отправляется на сервер.
- encodePriority(facility, priority)¶
- Кодирует средство и приоритет в целое число. Можно передавать строки или целые числа – если передаются строки, внутренние словари сопоставлений используются для преобразования их в целые числа.
NTEventLogHandler¶
Класс NTEventLogHandler, находящийся в модуле logging.handlers, поддерживает отправку сообщений журналирования в журнал событий локальной Windows NT, Windows 2000 или Windows XP. Перед использованием необходимо установить расширения Win32 от Марка Хэммонда для Python.
- class logging.handlers.NTEventLogHandler(appname[, dllname[, logtype]])¶
Возвращает новый экземпляр класса NTEventLogHandler. Параметр appname используется для определения имени приложения, отображаемого в журнале событий. С использованием этого имени создается соответствующая запись реестра. Параметр dllname должен содержать полный путь к .dll или .exe-файлу с определениями сообщений для хранения в журнале (если не указан, используется 'win32service.pyd' – он устанавливается вместе с расширениями Win32 и содержит несколько базовых определений сообщений-заполнителей. Обратите внимание, что использование этих заполнителей приведет к увеличению размера журнала, так как весь источник сообщений хранится в журнале. Если нужны более компактные журналы, следует передать имя собственного .dll или .exe, содержащего нужные определения сообщений). Параметр logtype может быть одним из 'Application', 'System' или 'Security', по умолчанию 'Application'.
- close()¶
- На этом этапе можно удалить имя приложения из реестра как источник записей журнала событий. Однако после этого события не будут отображаться в средстве просмотра журнала событий так, как предполагалось – для получения имени .dll требуется доступ к реестру. Текущая версия этого не делает.
- emit(record)¶
- Определяет идентификатор сообщения, категорию и тип события, а затем записывает сообщение в журнал событий NT.
- getEventCategory(record)¶
- Возвращает категорию события для записи. Переопределите этот метод, чтобы указать собственные категории. Данная версия возвращает 0.
- getEventType(record)¶
- Возвращает тип события для записи. Переопределите этот метод, если нужно задать собственные типы. В данной версии используется сопоставление через атрибут typemap обработчика, который устанавливается в __init__() в виде словаря, содержащего сопоставления для DEBUG, INFO, WARNING, ERROR и CRITICAL. При использовании собственных уровней потребуется либо переопределить этот метод, либо поместить подходящий словарь в атрибут typemap обработчика.
- getMessageID(record)¶
- Возвращает идентификатор сообщения для записи. При использовании собственных сообщений можно передать в логгер идентификатор msg вместо строки форматирования. Затем здесь можно выполнить поиск по словарю для получения идентификатора сообщения. Данная версия возвращает 1 – базовый идентификатор сообщения в win32service.pyd.
SMTPHandler¶
Класс SMTPHandler, находящийся в модуле logging.handlers, поддерживает отправку сообщений журналирования на адрес электронной почты через SMTP.
- class logging.handlers.SMTPHandler(mailhost, fromaddr, toaddrs, subject[, credentials])¶
Возвращает новый экземпляр класса SMTPHandler. Экземпляр инициализируется адресами отправителя, получателя и темой письма. Параметр toaddrs должен быть списком строк. Для указания нестандартного порта SMTP используется кортеж (host, port) в аргументе mailhost. Если передана строка, используется стандартный порт SMTP. Если SMTP-сервер требует аутентификации, можно указать кортеж (username, password) в аргументе credentials.
- emit(record)¶
- Форматирует запись и отправляет её указанным получателям.
- getSubject(record)¶
- Чтобы задать тему письма, зависящую от записи, переопределите этот метод.
MemoryHandler¶
Класс MemoryHandler, находящийся в модуле logging.handlers, поддерживает буферизацию записей журналирования в памяти с периодической отправкой их в обработчик target. Сброс происходит, когда буфер полон или когда появляется событие определённой важности или выше.
MemoryHandler является подклассом более общего BufferingHandler, который является абстрактным классом. Он буферизирует записи журналирования в памяти. Каждый раз, когда запись добавляется в буфер, вызывается shouldFlush() для проверки необходимости сброса буфера. Если сброс нужен, то flush() должен выполнить необходимые действия.
- class logging.handlers.BufferingHandler(capacity)¶
Инициализирует обработчик буфером указанной ёмкости.
- emit(record)¶
- Добавляет запись в буфер. Если shouldFlush() возвращает true, вызывает flush() для обработки буфера.
- flush()¶
- Вы можете переопределить этот метод, чтобы задать собственное поведение при сбросе. Текущая версия просто очищает буфер.
- shouldFlush(record)¶
- Возвращает true, если буфер заполнен до ёмкости. Этот метод можно переопределить для реализации собственных стратегий сброса.
- class logging.handlers.MemoryHandler(capacity[, flushLevel[, target]])¶
Возвращает новый экземпляр класса MemoryHandler. Экземпляр инициализируется с размером буфера capacity. Если flushLevel не указан, используется ERROR. Если target не указан, перед тем как обработчик начнёт работать, цель нужно задать с помощью setTarget().
- flush()¶
- Для MemoryHandler сброс означает просто отправку буферизированных записей целевому обработчику, если он есть. Переопределите, если требуется иное поведение.
- setTarget(target)¶
- Устанавливает целевой обработчик для данного обработчика.
- shouldFlush(record)¶
- Проверяет, заполнен ли буфер или поступила запись уровня flushLevel или выше.
HTTPHandler¶
Класс HTTPHandler, расположенный в модуле logging.handlers, поддерживает отправку сообщений журнала на веб-сервер, используя семантику GET или POST.
- class logging.handlers.HTTPHandler(host, url[, method])¶
Возвращает новый экземпляр класса HTTPHandler. Экземпляр инициализируется адресом хоста, URL и HTTP-методом. host может быть в формате host:port, если требуется указать определённый номер порта. Если method не указан, используется GET.
- emit(record)¶
- Отправляет запись на веб-сервер в виде словаря, закодированного в URL.
Объекты Formatter¶Formatter Objects
Formatter имеют следующие атрибуты и методы. Они отвечают за преобразование LogRecord в (обычно) строку, которая может интерпретироваться человеком или внешней системой. Базовый Formatter позволяет задать строку форматирования. Если она не указана, используется значение по умолчанию '%(message)s'.
Formatter можно инициализировать строкой форматирования, которая использует знание атрибутов LogRecord – например, упомянутое выше значение по умолчанию, использующее тот факт, что сообщение и аргументы пользователя уже предварительно отформатированы в атрибут message объекта LogRecord. Эта строка форматирования содержит стандартные ключи отображения в стиле % языка Python. Дополнительные сведения о форматировании строк см. в разделе Old String Formatting Operations.
Полезные ключи отображения в LogRecord на данный момент:
| Формат | Описание |
|---|---|
| %(name)s | Имя логгера (канала журналирования). |
| %(levelno)s | Числовой уровень логирования сообщения (DEBUG, INFO, WARNING, ERROR, CRITICAL). |
| %(levelname)s | Текстовый уровень логирования сообщения ('DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'). |
| %(pathname)s | Полный путь к исходному файлу, из которого был сделан вызов логирования (если доступен). |
| %(filename)s | Имя файла из пути. |
| %(module)s | Модуль (часть имени файла). |
| %(funcName)s | Имя функции, содержащей вызов логирования. |
| %(lineno)d | Номер строки исходного кода, из которой был сделан вызов логирования (если доступен). |
| %(created)f | Время создания LogRecord (возвращаемое time.time()). |
| %(relativeCreated)d | Время в миллисекундах, когда LogRecord был создан, относительно времени загрузки модуля logging. |
| %(asctime)s | Человекочитаемое время создания LogRecord. По умолчанию оно имеет вид “2003-07-08 16:49:45,896” (числа после запятой – миллисекунды времени). |
| %(msecs)d | Миллисекундная часть времени, когда был создан LogRecord. |
| %(поток)d | Идентификатор потока (если доступен). |
| %(threadName)s | Имя потока (если доступно). |
| %(процесс)d | Идентификатор процесса (если доступен). |
| %(message)s | Записанное сообщение, вычисляемое как msg % args. |
- class logging.Formatter([fmt[, datefmt]])¶
Возвращает новый экземпляр класса Formatter. Экземпляр инициализируется строкой форматирования для всего сообщения, а также строкой форматирования для части даты/времени сообщения. Если fmt не указан, используется '%(message)s'. Если datefmt не указан, используется формат даты ISO8601.
- format(record)¶
- Словарь атрибутов записи используется как операнд для операции форматирования строки. Возвращает результирующую строку. Перед форматированием словаря выполняется несколько подготовительных шагов. Атрибут message записи вычисляется с помощью msg % args. Если строка форматирования содержит '(asctime)', вызывается formatTime() для форматирования времени события. Если присутствует информация об исключении, она форматируется с помощью formatException() и добавляется к сообщению. Обратите внимание, что отформатированная информация об исключении кэшируется в атрибуте exc_text. Это полезно, поскольку информацию об исключении можно сериализовать и отправить по сети, но следует быть осторожным, если имеется более одного подкласса Formatter, который настраивает форматирование информации об исключении. В этом случае вам придется очищать кэшированное значение после того, как форматировщик выполнит свое форматирование, чтобы следующий форматировщик, обрабатывающий событие, не использовал кэшированное значение, а пересчитал его заново.
- formatTime(record[, datefmt])¶
- Этот метод должен вызываться из format() форматировщиком, который хочет использовать отформатированное время. Этот метод может быть переопределен в форматировщиках для любых специфических требований, но базовое поведение следующее: если указан datefmt (строка), он используется с time.strftime() для форматирования времени создания записи. В противном случае используется формат ISO8601. Возвращается результирующая строка.
- formatException(exc_info)¶
- Форматирует указанную информацию об исключении (стандартный кортеж исключения, возвращаемый sys.exc_info()) в виде строки. Эта реализация по умолчанию просто использует traceback.print_exception(). Полученная строка возвращается.
Объекты фильтров¶Filter Objects
Filter могут использоваться обработчиками Handler и регистраторами Logger для более тонкой фильтрации, чем предусмотрено уровнями. Базовый класс фильтра допускает только события, которые находятся ниже определённой точки в иерархии регистраторов. Например, фильтр, инициализированный строкой “A.B”, будет пропускать события, зарегистрированные регистраторами “A.B”, “A.B.C”, “A.B.C.D”, “A.B.D” и т.д., но не “A.BB”, “B.A.B” и т.д. Если инициализирован пустой строкой, пропускаются все события.
- class logging.Filter([name])¶
Возвращает экземпляр класса Filter. Если указан name, он задаёт имя регистратора, события которого (вместе с событиями его дочерних регистраторов) будут пропускаться через фильтр. Если имя не указано, пропускаются все события.
- filter(record)¶
- Следует ли регистрировать указанную запись? Возвращает ноль, если нет, и ненулевое значение, если да. Если это уместно, запись может быть изменена на месте данным методом.
Объекты LogRecord¶LogRecord Objects
Экземпляры LogRecord создаются каждый раз, когда что-либо регистрируется. Они содержат всю информацию, относящуюся к регистрируемому событию. Основная передаваемая информация находится в msg и args, которые объединяются с помощью msg % args для создания поля message записи. Запись также включает информацию, такую как время создания записи, исходную строку, в которой был выполнен вызов регистрации, и любую информацию об исключении, подлежащую регистрации.
- class logging.LogRecord(name, lvl, pathname, lineno, msg, args, exc_info[, func])¶
Возвращает экземпляр LogRecord, инициализированный информацией. name – имя регистратора; lvl – числовой уровень; pathname – абсолютный путь к исходному файлу, в котором был сделан вызов логирования; lineno – номер строки в этом файле, где находится вызов логирования; msg – сообщение, предоставленное пользователем (строка формата); args – кортеж, который вместе с msg составляет пользовательское сообщение; exc_info – кортеж исключения, полученный вызовом sys.exc_info() (или None, если информация об исключении недоступна). func – имя функции, из которой был сделан вызов логирования. Если не указано, по умолчанию None.
Объекты LoggerAdapter¶LoggerAdapter Objects
Экземпляры LoggerAdapter используются для удобной передачи контекстной информации в вызовы логирования. Пример использования см. в разделе добавление контекстной информации в вывод логирования.
- class logging.LoggerAdapter(logger, extra)¶
Возвращает экземпляр LoggerAdapter, инициализированный базовым экземпляром Logger и объектом, подобным словарю.
- process(msg, kwargs)¶
- Изменяет сообщение и/или именованные аргументы, переданные в вызов логирования, чтобы вставить контекстную информацию. Эта реализация берёт объект, переданный как extra конструктору, и добавляет его в kwargs с ключом 'extra'. Возвращаемое значение – кортеж (msg, kwargs), содержащий (возможно изменённые) версии переданных аргументов.
Помимо перечисленного, LoggerAdapter поддерживает все методы логирования класса Logger, то есть debug(), info(), warning(), error(), exception(), critical() и log(). Эти методы имеют те же сигнатуры, что и их аналоги в Logger, поэтому экземпляры обоих типов можно использовать взаимозаменямо.
Потокобезопасность¶Thread Safety
Модуль logging спроектирован так, чтобы быть потокобезопасным без каких-либо специальных действий требуемых от его клиентов. Это достигается за счёт использования threading блокировок; одна блокировка сериализует доступ к общим данным модуля, а каждый обработчик также создаёт блокировку для сериализации доступа к своему нижележащему вводу-выводу.
Конфигурация¶Configuration
Функции настройки¶Configuration functions
Следующие функции настраивают модуль логирования. Они находятся в модуле logging.config. Их использование необязательно – настроить модуль логирования можно как с помощью этих функций, так и напрямую через основной API (определённый в самом logging) и определением обработчиков, объявленных либо в logging, либо в logging.handlers.
- logging.fileConfig(fname[, defaults])¶
- Читает конфигурацию логирования из файла формата configparser с именем fname. Эту функцию можно вызывать несколько раз из приложения, что позволяет конечному пользователю выбирать из различных готовых конфигураций (если разработчик предоставляет механизм для отображения вариантов и загрузки выбранной конфигурации). Значения по умолчанию, передаваемые в ConfigParser, можно указать в аргументе defaults.
- logging.listen([port])¶
Запускает сокет-сервер на указанном порту и ожидает новые конфигурации. Если порт не указан, используется значение по умолчанию модуля DEFAULT_LOGGING_CONFIG_PORT. Конфигурации логирования отправляются в виде файла, пригодного для обработки функцией fileConfig(). Возвращает экземпляр поток, на котором можно вызвать start() для запуска сервера и к которому при необходимости можно применить join(). Чтобы остановить сервер, вызовите stopListening().
Чтобы отправить конфигурацию в сокет, прочитайте файл конфигурации и отправьте его в сокет в виде строки байтов, перед которой идёт четырёхбайтовая длина, упакованная в двоичном виде с помощью struct.pack('>L', n).
Формат файла конфигурации¶Configuration file format
The configuration file format understood by fileConfig() is based on configparser functionality. The file must contain sections called [loggers], [handlers] and [formatters] which identify by name the entities of each type which are defined in the file. For each such entity, there is a separate section which identifies how that entity is configured. Thus, for a logger named log01 in the [loggers] section, the relevant configuration details are held in a section [logger_log01]. Similarly, a handler called hand01 in the [handlers] section will have its configuration held in a section called [handler_hand01], while a formatter called form01 in the [formatters] section will have its configuration specified in a section called [formatter_form01]. The root logger configuration must be specified in a section called [logger_root].
Примеры таких разделов в файле приведены ниже.
[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
The level entry can be one of DEBUG, INFO, WARNING, ERROR, CRITICAL or NOTSET. For the root logger only, NOTSET means that all messages will be logged. Level values are eval()uated in the context of the logging package’s namespace.
The handlers entry is a comma-separated list of handler names, which must appear in the [handlers] section. These names must appear in the [handlers] section and have corresponding sections in the configuration file.
Для регистраторов, отличных от корневого, требуется некоторая дополнительная информация. Это проиллюстрировано следующим примером.
[logger_parser]
level=DEBUG
handlers=hand01
propagate=1
qualname=compiler.parser
The level and handlers entries are interpreted as for the root logger, except that if a non-root logger’s level is specified as NOTSET, the system consults loggers higher up the hierarchy to determine the effective level of the logger. The propagate entry is set to 1 to indicate that messages must propagate to handlers higher up the logger hierarchy from this logger, or 0 to indicate that messages are not propagated to handlers up the hierarchy. The qualname entry is the hierarchical channel name of the logger, that is to say the name used by the application to get the logger.
Разделы, которые задают конфигурацию обработчика, иллюстрируются следующим примером.
[handler_hand01]
class=StreamHandler
level=NOTSET
formatter=form01
args=(sys.stdout,)
Запись class указывает класс обработчика (определяемый с помощью eval() в пространстве имён пакета logging). Значение level интерпретируется так же, как для логгеров, а NOTSET означает «логировать всё».
The formatter entry indicates the key name of the formatter for this handler. If blank, a default formatter (logging._defaultFormatter) is used. If a name is specified, it must appear in the [formatters] section and have a corresponding section in the configuration file.
Запись args при вычислении 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')
[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')
Разделы, которые задают конфигурацию форматировщика, обычно выглядят следующим образом.
[formatter_form01]
format=F1 %(asctime)s %(levelname)s %(message)s
datefmt=
class=logging.Formatter
Запись format – это общая строка формата, а запись datefmt – это строка формата даты/времени, совместимая с strftime(). Если она пуста, пакет подставляет дату/время в формате ISO8601, что почти эквивалентно указанию строки формата даты "%Y-%m-%d %H:%M:%S". Формат ISO8601 также задаёт миллисекунды, которые добавляются к результату использования указанной выше строки формата через запятую. Пример времени в формате ISO8601: 2003-01-23 00:29:50,411.
Запись class необязательна. Она указывает имя класса форматировщика (в виде точечного имени модуля и класса). Эта опция полезна для создания экземпляра подкласса Formatter. Подклассы Formatter могут отображать трассировки исключений в расширенном или сжатом формате.
Пример сервера конфигурации¶Configuration server example
Вот пример модуля, использующего сервер конфигурации журналирования:
import logging
import logging.config
import time
import os
# Прочитать начальный конфигурационный файл.
logging.config.fileConfig("logging.conf")
# Создать и запустить слушатель на порту 9999.
t = logging.config.listen(9999)
t.start()
logger = logging.getLogger("simpleExample")
try:
# Прокрутить вызовы логирования, чтобы увидеть разницу.
# Создавать новые конфигурации до нажатия Ctrl+C.
while True:
logger.debug("debug message")
logger.info("info message")
logger.warn("warn message")
logger.error("error message")
logger.critical("critical message")
time.sleep(5)
except KeyboardInterrupt:
# очистка
logging.config.stopListening()
t.join()
А вот скрипт, который принимает имя файла и отправляет этот файл на сервер, предварив его двоично-закодированной длиной, в качестве новой конфигурации журналирования:
#!/usr/bin/env python
import socket, sys, struct
data_to_send = open(sys.argv[1], "r").read()
HOST = 'localhost'
PORT = 9999
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
print("connecting...")
s.connect((HOST, PORT))
print("sending config...")
s.send(struct.pack(">L", len(data_to_send)))
s.send(data_to_send)
s.close()
print("complete")
Дополнительные примеры¶More examples
Несколько обработчиков и форматировщиков¶Multiple handlers and formatters
Логгеры – это обычные объекты Python. У метода addHandler() нет минимального или максимального ограничения на количество добавляемых обработчиков. Иногда бывает полезно, чтобы приложение записывало все сообщения всех уровней в текстовый файл и одновременно выводило ошибки и выше на консоль. Чтобы это настроить, достаточно сконфигурировать соответствующие обработчики. Вызовы логирования в коде приложения останутся без изменений. Вот небольшая модификация предыдущего простого примера конфигурации на основе модуля:
import logging
logger = logging.getLogger("simple_example")
logger.setLevel(logging.DEBUG)
# создать файловый обработчик, записывающий даже отладочные сообщения
fh = logging.FileHandler("spam.log")
fh.setLevel(logging.DEBUG)
# создать консольный обработчик с более высоким уровнем логирования
ch = logging.StreamHandler()
ch.setLevel(logging.ERROR)
# создать форматтер и добавить его к обработчикам
formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
ch.setFormatter(formatter)
fh.setFormatter(formatter)
# добавить обработчики в логгер
logger.addHandler(ch)
logger.addHandler(fh)
# код "application"
logger.debug("debug message")
logger.info("info message")
logger.warn("warn message")
logger.error("error message")
logger.critical("critical message")
Обратите внимание, что код «приложения» безразличен к наличию нескольких обработчиков. Всё, что изменилось – это добавление и настройка нового обработчика с именем fh.
Возможность создавать новые обработчики с фильтрами более высокого или низкого уровня серьезности может быть очень полезна при написании и тестировании приложения. Вместо использования множества операторов print для отладки используйте logger.debug: в отличие от операторов print, которые вам придется удалить или закомментировать позже, операторы logger.debug могут оставаться в исходном коде и оставаться неактивными до тех пор, пока они снова не понадобятся. В этом случае единственное, что нужно сделать, – изменить уровень серьезности логгера и/или обработчика на debug.
Использование логирования в нескольких модулях¶Using logging in multiple modules
Выше упоминалось, что многократные вызовы logging.getLogger('someLogger') возвращают ссылку на один и тот же объект логгера. Это верно не только в пределах одного модуля, но и между модулями, пока они находятся в одном процессе интерпретатора Python. Это справедливо для ссылок на один и тот же объект; кроме того, код приложения может определить и настроить родительский логгер в одном модуле и создать (но не настраивать) дочерний логгер в другом модуле, и все вызовы логгера к дочернему будут передаваться родительскому. Вот пример главного модуля:
import logging
import auxiliary_module
# создать логгер с именем "spam_application"
logger = logging.getLogger("spam_application")
logger.setLevel(logging.DEBUG)
# создать файловый обработчик, записывающий даже отладочные сообщения
fh = logging.FileHandler("spam.log")
fh.setLevel(logging.DEBUG)
# создать консольный обработчик с более высоким уровнем логирования
ch = logging.StreamHandler()
ch.setLevel(logging.ERROR)
# создать форматтер и добавить его к обработчикам
formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
fh.setFormatter(formatter)
ch.setFormatter(formatter)
# добавить обработчики к логгеру
logger.addHandler(fh)
logger.addHandler(ch)
logger.info("creating an instance of auxiliary_module.Auxiliary")
a = auxiliary_module.Auxiliary()
logger.info("created an instance of auxiliary_module.Auxiliary")
logger.info("calling auxiliary_module.Auxiliary.do_something")
a.do_something()
logger.info("finished auxiliary_module.Auxiliary.do_something")
logger.info("calling auxiliary_module.some_function()")
auxiliary_module.some_function()
logger.info("done with auxiliary_module.some_function()")
А вот вспомогательный модуль:
import logging
# create logger
module_logger = logging.getLogger("spam_application.auxiliary")
class Auxiliary:
def __init__(self):
self.logger = logging.getLogger("spam_application.auxiliary.Auxiliary")
self.logger.info("creating an instance of Auxiliary")
def do_something(self):
self.logger.info("doing something")
a = 1 + 1
self.logger.info("done doing something")
def some_function():
module_logger.info("received a call to \"some_function\"")
Вывод выглядит так:
2005-03-23 23:47:11,663 - spam_application - INFO -
creating an instance of auxiliary_module.Auxiliary
2005-03-23 23:47:11,665 - spam_application.auxiliary.Auxiliary - INFO -
creating an instance of Auxiliary
2005-03-23 23:47:11,665 - spam_application - INFO -
created an instance of auxiliary_module.Auxiliary
2005-03-23 23:47:11,668 - spam_application - INFO -
calling auxiliary_module.Auxiliary.do_something
2005-03-23 23:47:11,668 - spam_application.auxiliary.Auxiliary - INFO -
doing something
2005-03-23 23:47:11,669 - spam_application.auxiliary.Auxiliary - INFO -
done doing something
2005-03-23 23:47:11,670 - spam_application - INFO -
finished auxiliary_module.Auxiliary.do_something
2005-03-23 23:47:11,671 - spam_application - INFO -
calling auxiliary_module.some_function()
2005-03-23 23:47:11,672 - spam_application.auxiliary - INFO -
received a call to "some_function"
2005-03-23 23:47:11,673 - spam_application - INFO -
done with auxiliary_module.some_function()