Документация Python неофициальный перевод

logging.md

2253 строк · 171.6 КБ · обычная страница · сырой текст · скачать

1> **Источник:** https://python-all.ru/3.0/library/logging.html2>3> «Документация Python на русском» – неофициальный перевод официальной документации Python: версии от 2.6 до 3.16, полнотекстовый поиск, английский оригинал рядом с переводом. Эта Markdown-версия страницы предназначена для работы с LLM: вставьте её в ChatGPT, Claude или Cursor.45---67# `logging` – Средство журналирования для Python89Этот модуль определяет функции и классы, реализующие гибкую систему логирования ошибок для приложений.1011Логирование выполняется вызовом методов экземпляров класса `Logger` (далее называемых *логгерами*). Каждый экземпляр имеет имя, и они концептуально организованы в иерархию пространств имён с использованием точек (.) в качестве разделителей. Например, логгер с именем «scan» является родительским для логгеров «scan.text», «scan.html» и «scan.pdf». Имена логгеров могут быть любыми и указывают на область приложения, из которой исходит регистрируемое сообщение.1213Регистрируемые сообщения также имеют связанные с ними уровни важности. Предоставляемые по умолчанию уровни: `DEBUG`, `INFO`, `WARNING`, `ERROR` и `CRITICAL`. Для удобства уровень важности сообщения указывается вызовом соответствующего метода `Logger`. Это методы [`debug()`](https://python-all.ru/3.0/library/logging.html#logging.debug), [`info()`](https://python-all.ru/3.0/library/logging.html#logging.info), [`warning()`](https://python-all.ru/3.0/library/logging.html#logging.warning), [`error()`](https://python-all.ru/3.0/library/logging.html#logging.error) и [`critical()`](https://python-all.ru/3.0/library/logging.html#logging.critical), которые соответствуют уровням по умолчанию. Вы не обязаны использовать только эти уровни: можно задать собственные и применять более общий метод `Logger` – [`log()`](https://python-all.ru/3.0/library/logging.html#logging.log), который принимает явный аргумент уровня.1415## Учебник по журналированию1617Ключевое преимущество наличия API логирования, предоставляемого модулем стандартной библиотеки, заключается в том, что все модули Python могут участвовать в логировании, поэтому журнал приложения может включать сообщения от сторонних модулей.1819Конечно, можно регистрировать сообщения с разными уровнями подробности или направлять их в разные места. Поддержка записи сообщений в файлы, по HTTP GET/POST, электронной почте через SMTP, через обычные сокеты или через механизмы логирования, специфичные для ОС, – всё это поддерживается стандартным модулем. Вы также можете создать собственный класс назначения журнала, если ваши особые требования не удовлетворяются ни одним из встроенных классов.2021### Простые примеры2223Большинство приложений, скорее всего, захотят вести журнал в файл, поэтому начнём с этого случая. Используя функцию [`basicConfig()`](https://python-all.ru/3.0/library/logging.html#logging.basicConfig), можно настроить обработчик по умолчанию так, чтобы отладочные сообщения записывались в файл:2425```python26import logging27LOG_FILENAME = '/tmp/logging_example.out'28logging.basicConfig(filename=LOG_FILENAME,level=logging.DEBUG,)2930logging.debug('This message should go to the log file')31```3233Теперь, если открыть файл и посмотреть, что в нём, мы должны обнаружить сообщение журнала:3435```python36DEBUG:root:This message should go to the log file37```3839Если запускать скрипт многократно, дополнительные сообщения журнала добавляются в конец файла. Чтобы каждый раз создавать новый файл, можно передать аргумент filemode функции [`basicConfig()`](https://python-all.ru/3.0/library/logging.html#logging.basicConfig) со значением `'w'`. Вместо того чтобы управлять размером файла самостоятельно, проще использовать `RotatingFileHandler`:4041```python42import glob43import logging44import logging.handlers4546LOG_FILENAME = '/tmp/logging_rotatingfile_example.out'4748# Настроить конкретный регистратор с нужным уровнем вывода49my_logger = logging.getLogger('MyLogger')50my_logger.setLevel(logging.DEBUG)5152# Добавить обработчик сообщений журнала в регистратор53handler = logging.handlers.RotatingFileHandler(54              LOG_FILENAME, maxBytes=20, backupCount=5)5556my_logger.addHandler(handler)5758# Записать несколько сообщений59for i in range(20):60    my_logger.debug('i = %d' % i)6162# Посмотреть, какие файлы созданы63logfiles = glob.glob('%s*' % LOG_FILENAME)6465for filename in logfiles:66    print(filename)67```6869В результате должно получиться 6 отдельных файлов, каждый из которых содержит часть истории журнала приложения:7071```python72/tmp/logging_rotatingfile_example.out73/tmp/logging_rotatingfile_example.out.174/tmp/logging_rotatingfile_example.out.275/tmp/logging_rotatingfile_example.out.376/tmp/logging_rotatingfile_example.out.477/tmp/logging_rotatingfile_example.out.578```7980Самый новый файл всегда называется `/tmp/logging_rotatingfile_example.out`, и каждый раз, когда он достигает предела размера, он переименовывается с суффиксом `.1`. Каждый из существующих резервных файлов переименовывается с увеличением суффикса (`.1` становится `.2` и т.д.), а файл `.5` удаляется.8182Очевидно, в этом примере максимальный размер файла установлен чрезвычайно малым для наглядности. Следует установить *maxBytes* в подходящее значение.8384Ещё одна полезная возможность API логирования – создавать разные сообщения на разных уровнях логирования. Это позволяет, например, наполнить код отладочными сообщениями, но снизить уровень логирования так, чтобы в рабочей системе эти сообщения не записывались. Уровни по умолчанию: `CRITICAL`, `ERROR`, `WARNING`, `INFO`, `DEBUG` и `NOTSET`.8586Логгер, обработчик и вызов сообщения – каждый задаёт свой уровень. Сообщение выводится только в том случае, если обработчик и логгер настроены на вывод сообщений этого уровня или ниже. Например, если сообщение имеет уровень `CRITICAL`, а логгер настроен на `ERROR`, сообщение будет выведено. Если сообщение имеет уровень `WARNING`, а логгер настроен на вывод только `ERROR`, сообщение не будет выведено:8788```python89import logging90import sys9192LEVELS = {'debug': logging.DEBUG,93          'info': logging.INFO,94          'warning': logging.WARNING,95          'error': logging.ERROR,96          'critical': logging.CRITICAL}9798if len(sys.argv) > 1:99    level_name = sys.argv[1]100    level = LEVELS.get(level_name, logging.NOTSET)101    logging.basicConfig(level=level)102103logging.debug('This is a debug message')104logging.info('This is an info message')105logging.warning('This is a warning message')106logging.error('This is an error message')107logging.critical('This is a critical error message')108```109110Запустите скрипт с аргументом, например, ‘debug’ или ‘warning’, чтобы увидеть, какие сообщения отображаются на разных уровнях:111112```python113$ python logging_level_example.py debug114DEBUG:root:This is a debug message115INFO:root:This is an info message116WARNING:root:This is a warning message117ERROR:root:This is an error message118CRITICAL:root:This is a critical error message119120$ python logging_level_example.py info121INFO:root:This is an info message122WARNING:root:This is a warning message123ERROR:root:This is an error message124CRITICAL:root:This is a critical error message125```126127Вы заметите, что во всех этих сообщениях журнала присутствует `root`. Модуль logging поддерживает иерархию логгеров с разными именами. Простой способ понять, откуда пришло конкретное сообщение, – использовать отдельный объект логгера для каждого модуля. Каждый новый логгер «наследует» конфигурацию своего родителя, а сообщения, отправленные логгеру, включают имя этого логгера. При желании каждый логгер можно настроить по-разному, чтобы сообщения от разных модулей обрабатывались различными способами. Рассмотрим простой пример логирования из разных модулей, чтобы легко было отследить источник сообщения:128129```python130import logging131132logging.basicConfig(level=logging.WARNING)133134logger1 = logging.getLogger('package1.module1')135logger2 = logging.getLogger('package2.module2')136137logger1.warning('This message comes from one module')138logger2.warning('And this message comes from another module')139```140141И вывод:142143```python144$ python logging_modules_example.py145WARNING:package1.module1:This message comes from one module146WARNING:package2.module2:And this message comes from another module147```148149Существует гораздо больше возможностей для настройки логирования, включая разные варианты форматирования сообщений, доставку сообщений в несколько мест и изменение конфигурации долго работающего приложения на лету через сокетный интерфейс. Все эти возможности подробно описаны в документации библиотечного модуля.150151### Логгеры152153Библиотека logging использует модульный подход и предлагает несколько категорий компонентов: логгеры, обработчики, фильтры и форматировщики. Логгеры предоставляют интерфейс, который напрямую используется кодом приложения. Обработчики отправляют записи журнала в соответствующее место назначения. Фильтры предоставляют более тонкое средство для определения того, какие записи журнала следует передать обработчику. Форматировщики задают структуру итоговой записи журнала.154155Объекты `Logger` выполняют тройную задачу. Во-первых, они предоставляют коду приложения несколько методов, чтобы приложения могли регистрировать сообщения во время выполнения. Во-вторых, объекты логгера определяют, на какие сообщения следует реагировать, на основе степени важности (стандартное средство фильтрации) или объектов-фильтров. В-третьих, объекты логгера передают соответствующие сообщения всем заинтересованным обработчикам.156157Наиболее часто используемые методы объектов логгера делятся на две категории: настройка и отправка сообщений.158159- [`Logger.setLevel()`](https://python-all.ru/3.0/library/logging.html#logging.Logger.setLevel) задаёт наименьшую степень важности сообщения, которое будет обрабатывать логгер; при этом debug – это наименьший встроенный уровень важности, а critical – наибольший. Например, если уровень важности установлен в info, логгер будет обрабатывать только сообщения info, warning, error и critical, игнорируя сообщения debug.160- [`Logger.addFilter()`](https://python-all.ru/3.0/library/logging.html#logging.Logger.addFilter) и [`Logger.removeFilter()`](https://python-all.ru/3.0/library/logging.html#logging.Logger.removeFilter) добавляют и удаляют объекты фильтров из объекта логгера. В данном учебном пособии фильтры не рассматриваются.161162После настройки объекта регистратора следующие методы создают сообщения журнала:163164- [`Logger.debug()`](https://python-all.ru/3.0/library/logging.html#logging.Logger.debug), [`Logger.info()`](https://python-all.ru/3.0/library/logging.html#logging.Logger.info), [`Logger.warning()`](https://python-all.ru/3.0/library/logging.html#logging.Logger.warning), [`Logger.error()`](https://python-all.ru/3.0/library/logging.html#logging.Logger.error) и [`Logger.critical()`](https://python-all.ru/3.0/library/logging.html#logging.Logger.critical) создают записи лога с сообщением и уровнем, соответствующими их именам методов. Сообщение – это строка формата, которая может содержать стандартный синтаксис подстановки строк `%s`, `%d`, `%f` и так далее. Остальные аргументы – список объектов, соответствующих полям подстановки в сообщении. Что касается `**kwargs`, методы логирования учитывают только ключевое слово `exc_info` и используют его для определения, нужно ли записывать информацию об исключении.165- [`Logger.exception()`](https://python-all.ru/3.0/library/logging.html#logging.Logger.exception) создает сообщение лога, аналогичное [`Logger.error()`](https://python-all.ru/3.0/library/logging.html#logging.Logger.error). Отличие в том, что [`Logger.exception()`](https://python-all.ru/3.0/library/logging.html#logging.Logger.exception) выводит вместе с ним трассировку стека. Вызывайте этот метод только из обработчика исключений.166- [`Logger.log()`](https://python-all.ru/3.0/library/logging.html#logging.Logger.log) принимает уровень лога в качестве явного аргумента. Это немного более многословно для записи сообщений, чем использование удобных методов уровня лога, перечисленных выше, но именно так производится логирование на пользовательских уровнях лога.167168[`getLogger()`](https://python-all.ru/3.0/library/logging.html#logging.getLogger) возвращает ссылку на экземпляр регистратора с указанным именем, если оно задано, или на `root`, если нет. Имена представляют собой иерархические структуры, разделённые точками. Многократные вызовы [`getLogger()`](https://python-all.ru/3.0/library/logging.html#logging.getLogger) с одним и тем же именем возвращают ссылку на тот же объект регистратора. Регистраторы, находящиеся ниже в иерархическом списке, являются дочерними по отношению к регистраторам выше по списку. Например, для регистратора с именем `foo` регистраторы с именами `foo.bar`, `foo.bar.baz` и `foo.bam` являются дочерними по отношению к `foo`. Дочерние регистраторы передают сообщения вверх своим родительским регистраторам. Из-за этого нет необходимости определять и настраивать все регистраторы, используемые приложением. Достаточно настроить регистратор верхнего уровня и создавать дочерние регистраторы по мере необходимости.169170### Обработчики171172Объекты `Handler` отвечают за отправку соответствующих сообщений журнала (на основе их важности) в указанное место назначения. Объекты логгера могут добавлять к себе ноль или более объектов обработчиков с помощью метода `addHandler()`. В качестве примера: приложение может захотеть отправлять все сообщения в файл журнала, все сообщения уровня error и выше – в stdout, а все сообщения уровня critical – на адрес электронной почты. Для этого требуется три отдельных обработчика, каждый из которых отвечает за отправку сообщений определённого уровня важности в определённое место.173174Стандартная библиотека включает довольно много типов обработчиков; в данном учебном пособии используются только [`StreamHandler`](https://python-all.ru/3.0/library/logging.html#logging.StreamHandler) и [`FileHandler`](https://python-all.ru/3.0/library/logging.html#logging.FileHandler).175176В обработчике очень мало методов, которые должны беспокоить разработчиков приложений. Единственные методы обработчика, актуальные для разработчиков, использующих встроенные объекты-обработчики (то есть не создающих собственные обработчики), – это следующие методы настройки:177178- Метод [`Handler.setLevel()`](https://python-all.ru/3.0/library/logging.html#logging.Handler.setLevel), как и в объектах регистраторов, задаёт наименьший уровень важности, который будет передан в соответствующее назначение. Зачем нужны два метода `setLevel()`? Уровень, установленный в регистраторе, определяет, сообщения какой важности он будет передавать своим обработчикам. Уровень, установленный в каждом обработчике, определяет, какие сообщения этот обработчик будет отправлять дальше. `setFormatter()` выбирает объект Formatter для использования этим обработчиком.179- `addFilter()` и `removeFilter()` соответственно добавляют и удаляют объекты фильтров у обработчиков.180181Код приложения не должен напрямую создавать экземпляры обработчиков и использовать их. Вместо этого класс `Handler` является базовым классом, который определяет интерфейс, который должны иметь все обработчики, и устанавливает некоторое поведение по умолчанию, которое дочерние классы могут использовать (или переопределять).182183### Форматировщики184185Объекты Formatter настраивают окончательный порядок, структуру и содержимое сообщения журнала. В отличие от базового класса `logging.Handler`, код приложения может создавать экземпляры классов форматировщиков, хотя при необходимости особого поведения можно создать подкласс форматировщика. Конструктор принимает два необязательных аргумента: строку формата сообщения и строку формата даты. Если строка формата сообщения не задана, по умолчанию используется исходное сообщение. Если строка формата даты не задана, используется следующий формат даты по умолчанию:186187```python188%Y-%m-%d %H:%M:%S189```190191с добавленными в конце миллисекундами.192193Строка формата сообщения использует подстановку строк в стиле `%(<dictionary key>)s`; возможные ключи описаны в разделе [*Объекты Formatter*](https://python-all.ru/3.0/library/logging.html#formatter-objects).194195Следующая строка формата сообщения будет записывать время в удобочитаемом формате, уровень серьёзности сообщения и содержимое сообщения в указанном порядке:196197```python198"%(asctime)s - %(levelname)s - %(message)s"199```200201### Настройка журналирования202203Программисты могут настраивать логирование двумя способами: явно создавать логгеры, обработчики и форматтеры в главном модуле с помощью перечисленных выше методов настройки (на Python) или создавая конфигурационный файл логирования. Следующий код – пример настройки очень простого логгера, обработчика консоли и простого форматтера в модуле Python:204205```python206import logging207208# создать логгер209logger = logging.getLogger("simple_example")210logger.setLevel(logging.DEBUG)211# создать обработчик консоли и установить уровень DEBUG212ch = logging.StreamHandler()213ch.setLevel(logging.DEBUG)214# создать форматтер215formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")216# добавить форматтер в ch217ch.setFormatter(formatter)218# добавить ch в логгер219logger.addHandler(ch)220221# код "application"222logger.debug("debug message")223logger.info("info message")224logger.warn("warn message")225logger.error("error message")226logger.critical("critical message")227```228229Запуск этого модуля из командной строки выдает следующий результат:230231```python232$ python simple_logging_module.py2332005-03-19 15:10:26,618 - simple_example - DEBUG - debug message2342005-03-19 15:10:26,620 - simple_example - INFO - info message2352005-03-19 15:10:26,695 - simple_example - WARNING - warn message2362005-03-19 15:10:26,697 - simple_example - ERROR - error message2372005-03-19 15:10:26,773 - simple_example - CRITICAL - critical message238```239240Следующий модуль Python создает логгер, обработчик и форматтер, почти идентичные приведенным в примере выше, с единственным отличием – имена объектов.241242```python243import logging244import logging.config245246logging.config.fileConfig("logging.conf")247248# создать логгер249logger = logging.getLogger("simpleExample")250251# код "application"252logger.debug("debug message")253logger.info("info message")254logger.warn("warn message")255logger.error("error message")256logger.critical("critical message")257```258259Вот файл logging.conf:260261```python262[loggers]263keys=root,simpleExample264265[handlers]266keys=consoleHandler267268[formatters]269keys=simpleFormatter270271[logger_root]272level=DEBUG273handlers=consoleHandler274275[logger_simpleExample]276level=DEBUG277handlers=consoleHandler278qualname=simpleExample279propagate=0280281[handler_consoleHandler]282class=StreamHandler283level=DEBUG284formatter=simpleFormatter285args=(sys.stdout,)286287[formatter_simpleFormatter]288format=%(asctime)s - %(name)s - %(levelname)s - %(message)s289datefmt=290```291292Результат почти идентичен примеру без файла конфигурации:293294```python295$ python simple_logging_config.py2962005-03-19 15:38:55,977 - simpleExample - DEBUG - debug message2972005-03-19 15:38:55,979 - simpleExample - INFO - info message2982005-03-19 15:38:56,054 - simpleExample - WARNING - warn message2992005-03-19 15:38:56,055 - simpleExample - ERROR - error message3002005-03-19 15:38:56,130 - simpleExample - CRITICAL - critical message301```302303Видно, что подход с файлом конфигурации имеет несколько преимуществ перед подходом с кодом Python: в первую очередь разделение конфигурации и кода, а также возможность для не-программистов легко изменять параметры журналирования.304305### Настройка журналирования для библиотеки306307При разработке библиотеки, которая использует логирование, необходимо уделить внимание её настройке. Если использующее приложение не использует логирование, а код библиотеки вызывает функции логирования, то в консоль выводится одноразовое сообщение “No handlers could be found for logger X.Y.Z”. Это сообщение предназначено для выявления ошибок в настройке логирования, но может сбить с толку разработчика приложения, который не знает о логировании в библиотеке.308309Помимо документирования того, как библиотека использует логирование, хороший способ настроить логирование библиотеки так, чтобы не появлялось ложное сообщение – добавить обработчик, который ничего не делает. Это предотвращает вывод сообщения, поскольку обработчик будет найден, но не производит никакого вывода. Если пользователь библиотеки настраивает логирование для приложения, то, предположительно, эта конфигурация добавит некоторые обработчики, и если уровни настроены правильно, то вызовы логирования в коде библиотеки будут отправлять вывод этим обработчикам, как обычно.310311Обработчик, который ничего не делает, можно просто определить следующим образом:312313```python314import logging315316class NullHandler(logging.Handler):317    def emit(self, record):318        pass319```320321Экземпляр этого обработчика следует добавить к корневому логгеру пространства имён логирования, используемого библиотекой. Если всё логирование библиотеки *foo* осуществляется с помощью логгеров с именами вида “foo.x.y”, то код:322323```python324import logging325326h = NullHandler()327logging.getLogger("foo").addHandler(h)328```329330должен дать желаемый эффект. Если организация выпускает несколько библиотек, то имя логгера можно указать как “orgname.foo” вместо просто “foo”.331332Новое в версии 3.1.333334Класс [`NullHandler`](https://python-all.ru/3.0/library/logging.html#logging.NullHandler) отсутствовал в предыдущих версиях, но теперь он включён, чтобы его не нужно было определять в коде библиотек.335336## Уровни журналирования337338Числовые значения уровней журналирования приведены в следующей таблице. Они представляют интерес в первую очередь, если вы хотите определить собственные уровни и хотите, чтобы они имели определенные значения относительно предопределенных уровней. Если вы определяете уровень с тем же числовым значением, он перезаписывает предопределенное значение; предопределенное имя теряется.339340| Уровень | Числовое значение |341| --- | --- |342| `CRITICAL` | 50 |343| `ERROR` | 40 |344| `WARNING` | 30 |345| `INFO` | 20 |346| `DEBUG` | 10 |347| `NOTSET` | 0 |348349Уровни также могут быть связаны с логгерами: они устанавливаются разработчиком или загружаются из сохраненной конфигурации журналирования. Когда на логгере вызывается метод журналирования, логгер сравнивает свой собственный уровень с уровнем, связанным с вызовом метода. Если уровень логгера выше уровня вызова метода, сообщение журналирования фактически не создается. Это базовый механизм, управляющий подробностью вывода журналирования.350351Сообщения логирования представляют собой экземпляры класса [`LogRecord`](https://python-all.ru/3.0/library/logging.html#logging.LogRecord). Когда логгер решает фактически зарегистрировать событие, из сообщения логирования создаётся экземпляр [`LogRecord`](https://python-all.ru/3.0/library/logging.html#logging.LogRecord).352353Сообщения журнала проходят через механизм диспетчеризации с помощью *обработчиков*, которые являются экземплярами подклассов класса `Handler`. Обработчики отвечают за то, чтобы записанное сообщение (в виде объекта [`LogRecord`](https://python-all.ru/3.0/library/logging.html#logging.LogRecord)) попало в определённое место (или набор мест), полезное для целевой аудитории этого сообщения (такой как конечные пользователи, сотрудники службы поддержки, системные администраторы, разработчики). Обработчикам передаются экземпляры [`LogRecord`](https://python-all.ru/3.0/library/logging.html#logging.LogRecord), предназначенные для конкретных назначений. Каждый регистратор может иметь ноль, один или несколько связанных с ним обработчиков (через метод `addHandler()` класса `Logger`). В дополнение к любым обработчикам, непосредственно связанным с регистратором, *вызываются все обработчики, связанные со всеми предками регистратора*, для диспетчеризации сообщения.354355Как и для логгеров, для обработчиков могут быть заданы уровни. Уровень обработчика действует как фильтр так же, как и уровень логгера. Если обработчик решает фактически диспетчеризировать событие, используется метод `emit()` для отправки сообщения в место назначения. Большинству определённых пользователем подклассов `Handler` потребуется переопределить этот `emit()`.356357## Полезные обработчики358359Помимо базового класса `Handler`, предоставляется множество полезных подклассов:3603611. Экземпляры [`StreamHandler`](https://python-all.ru/3.0/library/logging.html#logging.StreamHandler) отправляют сообщения об ошибках в потоки (файлоподобные объекты).3622. Экземпляры [`FileHandler`](https://python-all.ru/3.0/library/logging.html#logging.FileHandler) отправляют сообщения об ошибках в файлы на диске.3633641. `BaseRotatingHandler` – это базовый класс для обработчиков, которые выполняют ротацию файлов журнала в определённый момент. Он не предназначен для прямого создания экземпляров. Вместо этого используйте [`RotatingFileHandler`](https://python-all.ru/3.0/library/logging.html#logging.handlers.RotatingFileHandler) или [`TimedRotatingFileHandler`](https://python-all.ru/3.0/library/logging.html#logging.handlers.TimedRotatingFileHandler).3652. Экземпляры [`RotatingFileHandler`](https://python-all.ru/3.0/library/logging.html#logging.handlers.RotatingFileHandler) отправляют сообщения об ошибках в файлы на диске с поддержкой максимального размера файлов журнала и ротации файлов журнала.3663. Экземпляры [`TimedRotatingFileHandler`](https://python-all.ru/3.0/library/logging.html#logging.handlers.TimedRotatingFileHandler) отправляют сообщения об ошибках в файлы на диске, осуществляя ротацию файла журнала через определённые временные интервалы.3674. Экземпляры [`SocketHandler`](https://python-all.ru/3.0/library/logging.html#logging.handlers.SocketHandler) отправляют сообщения об ошибках в сокеты TCP/IP .3685. Экземпляры [`DatagramHandler`](https://python-all.ru/3.0/library/logging.html#logging.handlers.DatagramHandler) отправляют сообщения об ошибках в сокеты UDP .3696. Экземпляры [`SMTPHandler`](https://python-all.ru/3.0/library/logging.html#logging.handlers.SMTPHandler) отправляют сообщения об ошибках на указанный адрес электронной почты.3707. Экземпляры [`SysLogHandler`](https://python-all.ru/3.0/library/logging.html#logging.handlers.SysLogHandler) отправляют сообщения об ошибках демону syslog Unix , возможно на удалённой машине.3718. Экземпляры [`NTEventLogHandler`](https://python-all.ru/3.0/library/logging.html#logging.handlers.NTEventLogHandler) отправляют сообщения об ошибках в журнал событий Windows NT/2000/XP.3729. Экземпляры [`MemoryHandler`](https://python-all.ru/3.0/library/logging.html#logging.handlers.MemoryHandler) отправляют сообщения об ошибках в буфер в памяти, который сбрасывается при выполнении определённых критериев.37310. Экземпляры [`HTTPHandler`](https://python-all.ru/3.0/library/logging.html#logging.handlers.HTTPHandler) отправляют сообщения об ошибках на HTTP сервер, используя семантику `GET` или `POST`.37411. Экземпляры [`WatchedFileHandler`](https://python-all.ru/3.0/library/logging.html#logging.handlers.WatchedFileHandler) следят за файлом, в который ведут журнал. Если файл изменяется, он закрывается и открывается заново по тому же имени. Этот обработчик полезен только в Unix-подобных системах; Windows не поддерживает используемый механизм.3753761. Экземпляры [`NullHandler`](https://python-all.ru/3.0/library/logging.html#logging.NullHandler) ничего не делают с сообщениями об ошибках. Они используются разработчиками библиотек, которые хотят использовать логирование, но избежать сообщения “No handlers could be found for logger XXX”, которое может отображаться, если пользователь библиотеки не настроил логирование. См. [*Настройка логирования для библиотеки*](https://python-all.ru/3.0/library/logging.html#library-config) для получения дополнительной информации.377378Новое в версии 3.1.379380Класс [`NullHandler`](https://python-all.ru/3.0/library/logging.html#logging.NullHandler) отсутствовал в предыдущих версиях.381382Классы [`NullHandler`](https://python-all.ru/3.0/library/logging.html#logging.NullHandler), [`StreamHandler`](https://python-all.ru/3.0/library/logging.html#logging.StreamHandler) и [`FileHandler`](https://python-all.ru/3.0/library/logging.html#logging.FileHandler) определены в основном пакете logging. Остальные обработчики определены в подмодуле `logging.handlers`. (Существует также ещё один подмодуль, `logging.config`, для функциональности настройки.)383384Зарегистрированные сообщения форматируются для отображения через экземпляры класса [`Formatter`](https://python-all.ru/3.0/library/logging.html#logging.Formatter). Они инициализируются форматной строкой, подходящей для использования с оператором % и словарём.385386Для форматирования нескольких сообщений в одном пакете можно использовать экземпляры `BufferingFormatter`. Помимо строки формата (которая применяется к каждому сообщению в пакете), предусмотрены строки формата для заголовка и завершающей части.387388Когда фильтрации на основе уровня логгера и/или уровня обработчика недостаточно, экземпляры [`Filter`](https://python-all.ru/3.0/library/logging.html#logging.Filter) можно добавить как к экземплярам `Logger`, так и к `Handler` (через их метод `addFilter()`). Прежде чем решить обрабатывать сообщение дальше, и логгеры, и обработчики запрашивают разрешение у всех своих фильтров. Если любой фильтр возвращает ложное значение, сообщение дальше не обрабатывается.389390Базовая функциональность [`Filter`](https://python-all.ru/3.0/library/logging.html#logging.Filter) позволяет фильтровать по конкретному имени средства журналирования. Если эта возможность используется, через фильтр пропускаются сообщения, отправленные указанному средству журналирования и его дочерним элементам, а все остальные отбрасываются.391392## Функции уровня модуля393394В дополнение к классам, описанным выше, существует ряд функций уровня модуля.395396#### `[logging.getLogger]logging.getLogger([name])`397398Возвращает регистратор с указанным именем или, если имя не указано, возвращает регистратор, который является корневым регистратором иерархии. Если указано, имя обычно представляет собой иерархическое имя, разделённое точками, например *“a”*, *“a.b”* или *“a.b.c.d”*. Выбор этих имён полностью зависит от разработчика, использующего логирование.399400Все вызовы этой функции с одним и тем же именем возвращают один и тот же экземпляр логгера. Это означает, что экземпляры логгера никогда не нужно передавать между различными частями приложения.401402#### `[logging.getLoggerClass]logging.getLoggerClass()`403404Возвращает либо стандартный класс `Logger`, либо последний класс, переданный в [`setLoggerClass()`](https://python-all.ru/3.0/library/logging.html#logging.setLoggerClass). Эта функция может быть вызвана из определения нового класса, чтобы гарантировать, что установка пользовательского класса `Logger` не отменит настройки, уже применённые другим кодом. Например:405406```python407class MyLogger(logging.getLoggerClass()):408    # ... переопределить поведение здесь409```410411#### `[logging.debug]logging.debug(msg[, *args[, **kwargs]])`412413Записывает сообщение с уровнем `DEBUG` в корневой регистратор. *msg* – это форматная строка сообщения, а *args* – аргументы, которые объединяются с *msg* с помощью оператора форматирования строк. (Обратите внимание, что это означает, что в форматной строке можно использовать ключевые слова вместе с единственным аргументом-словарём.)414415В *kwargs* проверяются два именованных аргумента: *exc\_info*, который, если он не равен false, приводит к добавлению информации об исключении в сообщение логирования. Если предоставлен кортеж исключения (в формате, возвращаемом [`sys.exc_info()`](https://python-all.ru/3.0/library/sys.html#sys.exc_info)), он используется; в противном случае вызывается [`sys.exc_info()`](https://python-all.ru/3.0/library/sys.html#sys.exc_info) для получения информации об исключении.416417Другим необязательным именованным аргументом является *extra*, который можно использовать для передачи словаря, которым заполняется \_\_dict\_\_ объекта LogRecord, созданного для события логирования, пользовательскими атрибутами. Затем эти пользовательские атрибуты можно использовать по своему усмотрению. Например, их можно включать в логируемые сообщения. Например:418419```python420FORMAT = "%(asctime)-15s %(clientip)s %(user)-8s %(message)s"421logging.basicConfig(format=FORMAT)422d = {'clientip': '192.168.0.1', 'user': 'fbloggs'}423logging.warning("Protocol problem: %s", "connection reset", extra=d)424```425426выведет что-то вроде427428```python4292006-02-08 22:20:02,165 192.168.0.1 fbloggs  Protocol problem: connection reset430```431432Ключи в словаре, переданном в *extra*, не должны пересекаться с ключами, используемыми системой логирования. (См. документацию [`Formatter`](https://python-all.ru/3.0/library/logging.html#logging.Formatter) для получения дополнительной информации о том, какие ключи используются системой логирования.)433434Если вы решите использовать эти атрибуты в логируемых сообщениях, нужно соблюдать осторожность. Например, в приведённом выше примере [`Formatter`](https://python-all.ru/3.0/library/logging.html#logging.Formatter) настроен с форматной строкой, которая ожидает 'clientip' и 'user' в словаре атрибутов LogRecord. Если они отсутствуют, сообщение не будет записано, так как возникнет исключение форматирования строки. Поэтому в этом случае всегда нужно передавать словарь *extra* с этими ключами.435436Хотя это может быть раздражающим, эта функция предназначена для использования в специализированных обстоятельствах, таких как многопоточные серверы, где один и тот же код выполняется во многих контекстах, и интересующие условия зависят от этого контекста (например, IP-адрес удалённого клиента и имя аутентифицированного пользователя в приведённом выше примере). В таких обстоятельствах, скорее всего, будут использоваться специализированные [`Formatter`](https://python-all.ru/3.0/library/logging.html#logging.Formatter) с конкретными `Handler`.437438#### `[logging.info]logging.info(msg[, *args[, **kwargs]])`439440Записывает сообщение с уровнем441442`INFO`443444в корневой регистратор. Аргументы интерпретируются так же, как для445446[`debug()`](https://python-all.ru/3.0/library/logging.html#logging.debug)447448.449450#### `[logging.warning]logging.warning(msg[, *args[, **kwargs]])`451452Регистрирует сообщение с уровнем453454`WARNING`455456в корневом логгере. Аргументы интерпретируются так же, как для457458[`debug()`](https://python-all.ru/3.0/library/logging.html#logging.debug)459460.461462#### `[logging.error]logging.error(msg[, *args[, **kwargs]])`463464Записывает сообщение с уровнем465466`ERROR`467468в корневой регистратор. Аргументы интерпретируются так же, как для469470[`debug()`](https://python-all.ru/3.0/library/logging.html#logging.debug)471472.473474#### `[logging.critical]logging.critical(msg[, *args[, **kwargs]])`475476Регистрирует сообщение с уровнем477478`CRITICAL`479480в корневом логгере. Аргументы интерпретируются так же, как для481482[`debug()`](https://python-all.ru/3.0/library/logging.html#logging.debug)483484.485486#### `[logging.exception]logging.exception(msg[, *args])`487488Регистрирует сообщение с уровнем489490`ERROR`491492в корневом логгере. Аргументы интерпретируются так же, как для493494[`debug()`](https://python-all.ru/3.0/library/logging.html#logging.debug)495496. Информация об исключении добавляется в сообщение журнала. Эта функция должна вызываться только из обработчика исключений.497498#### `[logging.log]logging.log(level, msg[, *args[, **kwargs]])`499500Регистрирует сообщение с уровнем501502*level*503504в корневом логгере. Остальные аргументы интерпретируются так же, как для505506[`debug()`](https://python-all.ru/3.0/library/logging.html#logging.debug)507508.509510#### `[logging.disable]logging.disable(lvl)`511512Задаёт переопределяющий уровень513514*lvl*515516для всех логгеров, который имеет приоритет над собственным уровнем логгера. Когда возникает необходимость временно снизить объём вывода логирования во всём приложении, эта функция может быть полезна.517518#### `[logging.addLevelName]logging.addLevelName(lvl, levelName)`519520Связывает уровень521522*lvl*523524с текстом525526*levelName*527528во внутреннем словаре, который используется для сопоставления числовых уровней с текстовым представлением, например, когда529530[`Formatter`](https://python-all.ru/3.0/library/logging.html#logging.Formatter)531532форматирует сообщение. Эта функция также может использоваться для определения собственных уровней. Единственные ограничения: все используемые уровни должны быть зарегистрированы с помощью этой функции, уровни должны быть положительными целыми числами и должны возрастать в порядке увеличения серьёзности.533534#### `[logging.getLevelName]logging.getLevelName(lvl)`535536Возвращает текстовое представление уровня логирования537538*lvl*539540. Если уровень является одним из предопределённых уровней541542`CRITICAL`543544,545546`ERROR`547548,549550`WARNING`551552,553554`INFO`555556или557558`DEBUG`559560, то возвращается соответствующая строка. Если вы связали уровни с именами с помощью561562[`addLevelName()`](https://python-all.ru/3.0/library/logging.html#logging.addLevelName)563564, то возвращается имя, которое вы связали с565566*lvl*567568. Если передаётся числовое значение, соответствующее одному из определённых уровней, возвращается соответствующее строковое представление. В противном случае возвращается строка «Level %s» % lvl.569570#### `[logging.makeLogRecord]logging.makeLogRecord(attrdict)`571572Создаёт и возвращает новый экземпляр573574[`LogRecord`](https://python-all.ru/3.0/library/logging.html#logging.LogRecord)575576, атрибуты которого определяются577578*attrdict*579580. Эта функция полезна для приёма сериализованного (pickled)581582[`LogRecord`](https://python-all.ru/3.0/library/logging.html#logging.LogRecord)583584словаря атрибутов, отправленного через сокет, и восстановления его как экземпляра585586[`LogRecord`](https://python-all.ru/3.0/library/logging.html#logging.LogRecord)587588на принимающей стороне.589590#### `[logging.basicConfig]logging.basicConfig([**kwargs])`591592Выполняет базовую настройку системы логирования, создавая [`StreamHandler`](https://python-all.ru/3.0/library/logging.html#logging.StreamHandler) со стандартным [`Formatter`](https://python-all.ru/3.0/library/logging.html#logging.Formatter) и добавляя его в корневой логгер. Функция ничего не делает, если для корневого логгера уже определены какие-либо обработчики. Функции [`debug()`](https://python-all.ru/3.0/library/logging.html#logging.debug), [`info()`](https://python-all.ru/3.0/library/logging.html#logging.info), [`warning()`](https://python-all.ru/3.0/library/logging.html#logging.warning), [`error()`](https://python-all.ru/3.0/library/logging.html#logging.error) и [`critical()`](https://python-all.ru/3.0/library/logging.html#logging.critical) автоматически вызывают [`basicConfig()`](https://python-all.ru/3.0/library/logging.html#logging.basicConfig), если для корневого логгера не определены обработчики.593594Поддерживаются следующие аргументы ключевых слов.595596| Формат | Описание |597| --- | --- |598| `filename` | Указывает, что создаётся FileHandler, с использованием указанного имени файла, а не StreamHandler. |599| `filemode` | Задает режим открытия файла, если указан filename (если filemode не указан, по умолчанию используется ‘a’). |600| `format` | Использует указанную строку формата для обработчика. |601| `datefmt` | Использовать указанный формат даты/времени. |602| `level` | Установить уровень корневого регистратора на указанный уровень. |603| `поток данных` | Использовать указанный поток данных для инициализации StreamHandler. Обратите внимание, что этот аргумент несовместим с «filename» – если оба присутствуют, «поток данных» игнорируется. |604605#### `[logging.shutdown]logging.shutdown()`606607Сообщает системе логирования о необходимости выполнить упорядоченное завершение работы, сбрасывая и закрывая все обработчики. Эту функцию следует вызывать при завершении приложения, и после этого вызова не следует использовать систему логирования.608609#### `[logging.setLoggerClass]logging.setLoggerClass(klass)`610611Указывает системе логирования использовать класс612613*klass*614615при создании экземпляра логгера. Класс должен определять616617[`__init__()`](https://python-all.ru/3.0/reference/datamodel.html#object.__init__)618619таким образом, чтобы требовался только аргумент name, а620621[`__init__()`](https://python-all.ru/3.0/reference/datamodel.html#object.__init__)622623должен вызывать624625`Logger.__init__()`626627. Эта функция обычно вызывается до создания любых логгеров приложениями, которым требуется использовать собственное поведение логгера.628629> **См. также**630>631> **[**PEP 282**](https://python-all.ru/3.0/library/logging.html) – Система логирования**632>633> Предложение, описывающее эту возможность для включения в стандартную библиотеку Python.634>635> **[Оригинальный пакет логирования Python](https://python-all.ru/3.0/library/logging.html)**636>637> Это исходный код пакета638>639> `logging`640>641> . Версия, доступная на этом сайте, подходит для Python 1.5.2, 2.1.x и 2.2.x, в которых пакет642>643> `logging`644>645> не входит в стандартную библиотеку.646647## Объекты Logger648649Логгеры имеют следующие атрибуты и методы. Обратите внимание, что логгеры никогда не создаются напрямую, а всегда через функцию уровня модуля `logging.getLogger(name)`.650651#### `[logging.Logger.propagate]Logger.propagate`652653Если это значение равно false, то сообщения логирования не передаются этим логгером или дочерними логгерами логгерам более высокого уровня (предкам). Конструктор устанавливает этот атрибут в 1.654655#### `[logging.Logger.setLevel]Logger.setLevel(lvl)`656657Устанавливает порог для этого логгера в *lvl*. Сообщения логирования, уровень которых ниже *lvl*, будут игнорироваться. При создании логгера уровень устанавливается в `NOTSET` (что приводит к обработке всех сообщений, если логгер является корневым, или к делегированию родительскому логгеру, если логгер не корневой). Обратите внимание, что корневой логгер создаётся с уровнем `WARNING`.658659Термин «делегирование родителю» означает, что если логгер имеет уровень NOTSET, то просматривается цепочка его логгеров-предков, пока не будет найден предок с уровнем, отличным от NOTSET, или не будет достигнут корневой логгер.660661Если найден предок с уровнем, отличным от NOTSET, то его уровень считается действующим уровнем регистратора, с которого начался поиск предка, и используется для определения обработки события логирования.662663Если достигнут корень и его уровень NOTSET, то будут обработаны все сообщения. В противном случае уровень корня будет использоваться как действующий уровень.664665#### `[logging.Logger.isEnabledFor]Logger.isEnabledFor(lvl)`666667Указывает, будет ли сообщение с уровнем серьёзности668669*lvl*670671обработано этим логгером. Этот метод сначала проверяет уровень, установленный на уровне модуля с помощью672673`logging.disable(lvl)`674675, а затем эффективный уровень логгера, определяемый676677[`getEffectiveLevel()`](https://python-all.ru/3.0/library/logging.html#logging.Logger.getEffectiveLevel)678679.680681#### `[logging.Logger.getEffectiveLevel]Logger.getEffectiveLevel()`682683Указывает эффективный уровень для данного логгера. Если с помощью684685[`setLevel()`](https://python-all.ru/3.0/library/logging.html#logging.Logger.setLevel)686687было установлено значение, отличное от688689`NOTSET`690691, возвращается оно. В противном случае выполняется обход иерархии к корню, пока не будет найдено значение, отличное от692693`NOTSET`694695, и возвращается это значение.696697#### `[logging.Logger.debug]Logger.debug(msg[, *args[, **kwargs]])`698699Записывает сообщение уровня `DEBUG` в этот логгер. *msg* – это строка форматирования сообщения, а *args* – аргументы, которые подставляются в *msg* с помощью оператора форматирования строк. (Обратите внимание, что это означает, что можно использовать ключевые слова в строке форматирования вместе с одним словарным аргументом.)700701В *kwargs* проверяются два именованных аргумента: *exc\_info*, который, если он не равен false, приводит к добавлению информации об исключении в сообщение логирования. Если предоставлен кортеж исключения (в формате, возвращаемом [`sys.exc_info()`](https://python-all.ru/3.0/library/sys.html#sys.exc_info)), он используется; в противном случае вызывается [`sys.exc_info()`](https://python-all.ru/3.0/library/sys.html#sys.exc_info) для получения информации об исключении.702703Другим необязательным именованным аргументом является *extra*, который можно использовать для передачи словаря, которым заполняется \_\_dict\_\_ объекта LogRecord, созданного для события логирования, пользовательскими атрибутами. Затем эти пользовательские атрибуты можно использовать по своему усмотрению. Например, их можно включать в логируемые сообщения. Например:704705```python706FORMAT = "%(asctime)-15s %(clientip)s %(user)-8s %(message)s"707logging.basicConfig(format=FORMAT)708d = { 'clientip' : '192.168.0.1', 'user' : 'fbloggs' }709logger = logging.getLogger("tcpserver")710logger.warning("Protocol problem: %s", "connection reset", extra=d)711```712713выведет что-то вроде714715```python7162006-02-08 22:20:02,165 192.168.0.1 fbloggs  Protocol problem: connection reset717```718719Ключи в словаре, переданном в *extra*, не должны пересекаться с ключами, используемыми системой логирования. (См. документацию [`Formatter`](https://python-all.ru/3.0/library/logging.html#logging.Formatter) для получения дополнительной информации о том, какие ключи используются системой логирования.)720721Если вы решите использовать эти атрибуты в логируемых сообщениях, нужно соблюдать осторожность. Например, в приведённом выше примере [`Formatter`](https://python-all.ru/3.0/library/logging.html#logging.Formatter) настроен с форматной строкой, которая ожидает 'clientip' и 'user' в словаре атрибутов LogRecord. Если они отсутствуют, сообщение не будет записано, так как возникнет исключение форматирования строки. Поэтому в этом случае всегда нужно передавать словарь *extra* с этими ключами.722723Хотя это может быть раздражающим, эта функция предназначена для использования в специализированных обстоятельствах, таких как многопоточные серверы, где один и тот же код выполняется во многих контекстах, и интересующие условия зависят от этого контекста (например, IP-адрес удалённого клиента и имя аутентифицированного пользователя в приведённом выше примере). В таких обстоятельствах, скорее всего, будут использоваться специализированные [`Formatter`](https://python-all.ru/3.0/library/logging.html#logging.Formatter) с конкретными `Handler`.724725#### `[logging.Logger.info]Logger.info(msg[, *args[, **kwargs]])`726727Записывает сообщение уровня728729`INFO`730731в этот логгер. Аргументы интерпретируются так же, как для732733[`debug()`](https://python-all.ru/3.0/library/logging.html#logging.debug)734735.736737#### `[logging.Logger.warning]Logger.warning(msg[, *args[, **kwargs]])`738739Записывает сообщение уровня740741`WARNING`742743в этот логгер. Аргументы интерпретируются так же, как для744745[`debug()`](https://python-all.ru/3.0/library/logging.html#logging.debug)746747.748749#### `[logging.Logger.error]Logger.error(msg[, *args[, **kwargs]])`750751Записывает сообщение уровня752753`ERROR`754755в этот логгер. Аргументы интерпретируются так же, как для756757[`debug()`](https://python-all.ru/3.0/library/logging.html#logging.debug)758759.760761#### `[logging.Logger.critical]Logger.critical(msg[, *args[, **kwargs]])`762763Записывает сообщение с уровнем764765`CRITICAL`766767в этот логгер. Аргументы интерпретируются так же, как для768769[`debug()`](https://python-all.ru/3.0/library/logging.html#logging.debug)770771.772773#### `[logging.Logger.log]Logger.log(lvl, msg[, *args[, **kwargs]])`774775Записывает сообщение с целочисленным уровнем776777*lvl*778779в этот логгер. Остальные аргументы интерпретируются так же, как для780781[`debug()`](https://python-all.ru/3.0/library/logging.html#logging.debug)782783.784785#### `[logging.Logger.exception]Logger.exception(msg[, *args])`786787Записывает сообщение с уровнем788789`ERROR`790791в этот логгер. Аргументы интерпретируются так же, как для792793[`debug()`](https://python-all.ru/3.0/library/logging.html#logging.debug)794795. Информация об исключении добавляется в сообщение лога. Этот метод следует вызывать только из обработчика исключений.796797#### `[logging.Logger.addFilter]Logger.addFilter(filt)`798799Добавляет указанный фильтр800801*filt*802803к этому логгеру.804805#### `[logging.Logger.removeFilter]Logger.removeFilter(filt)`806807Удаляет указанный фильтр808809*filt*810811из этого логгера.812813#### `[logging.Logger.filter]Logger.filter(record)`814815Применяет фильтры этого регистратора к записи и возвращает истинное значение, если запись должна быть обработана.816817#### `[logging.Logger.addHandler]Logger.addHandler(hdlr)`818819Добавляет указанный обработчик820821*hdlr*822823к этому логгеру.824825#### `[logging.Logger.removeHandler]Logger.removeHandler(hdlr)`826827Удаляет указанный обработчик828829*hdlr*830831из этого логгера.832833#### `[logging.Logger.findCaller]Logger.findCaller()`834835Находит имя исходного файла и номер строки вызывающего кода. Возвращает имя файла, номер строки и имя функции в виде кортежа из трёх элементов.836837#### `[logging.Logger.handle]Logger.handle(record)`838839Обрабатывает запись, передавая её всем обработчикам, связанным с этим логгером и его предками (пока не будет найдено ложное значение840841*propagate*842843). Этот метод используется для распакованных записей, полученных из сокета, а также для записей, созданных локально. Фильтрация на уровне логгера применяется с помощью844845[`filter()`](https://python-all.ru/3.0/library/functions.html#filter)846847.848849#### `[logging.Logger.makeRecord]Logger.makeRecord(name, lvl, fn, lno, msg, args, exc_info[, func, extra])`850851Это фабричный метод, который можно переопределить в подклассах для создания специализированных экземпляров852853[`LogRecord`](https://python-all.ru/3.0/library/logging.html#logging.LogRecord)854855.856857## Простой пример858859Пакет `logging` предоставляет большую гибкость, и его настройка может показаться пугающей. В этом разделе показано, что простое использование пакета logging вполне возможно.860861Самый простой пример показывает вывод сообщений в консоль:862863```python864import logging865866logging.debug('A debug message')867logging.info('Some information')868logging.warning('A shot across the bows')869```870871Если запустить приведённый выше скрипт, результат будет таким:872873```python874WARNING:root:A shot across the bows875```876877Поскольку не был указан конкретный регистратор, система использовала корневой регистратор. Сообщения уровней debug и info не появились, потому что по умолчанию корневой регистратор настроен на обработку только сообщений с уровнем WARNING и выше. Формат сообщений также задаётся по умолчанию, как и место вывода сообщений – `sys.stderr`. Уровень, формат и место вывода можно легко изменить, как показано в примере ниже:878879```python880import logging881882logging.basicConfig(level=logging.DEBUG,883                    format='%(asctime)s %(levelname)s %(message)s',884                    filename='/tmp/myapp.log',885                    filemode='w')886logging.debug('A debug message')887logging.info('Some information')888logging.warning('A shot across the bows')889```890891Метод [`basicConfig()`](https://python-all.ru/3.0/library/logging.html#logging.basicConfig) используется для изменения настроек конфигурации по умолчанию, в результате чего вывод (записываемый в `/tmp/myapp.log`) должен выглядеть примерно следующим образом:892893```python8942004-07-02 13:00:08,743 DEBUG A debug message8952004-07-02 13:00:08,743 INFO Some information8962004-07-02 13:00:08,743 WARNING A shot across the bows897```898899На этот раз были обработаны все сообщения с уровнем DEBUG и выше, был изменён формат сообщений, а вывод пошёл в указанный файл, а не в консоль.900901Форматирование использует старый стиль форматирования строк Python – см. раздел [*Старые операции форматирования строк*](https://python-all.ru/3.0/library/stdtypes.html#old-string-formatting). Строка формата принимает следующие общие спецификаторы. Полный список спецификаторов можно найти в документации [`Formatter`](https://python-all.ru/3.0/library/logging.html#logging.Formatter).902903| Формат | Описание |904| --- | --- |905| `%(name)s` | Имя логгера (канала журналирования). |906| `%(levelname)s` | Текстовый уровень логирования сообщения (`'DEBUG'`, `'INFO'`, `'WARNING'`, `'ERROR'`, `'CRITICAL'`). |907| `%(asctime)s` | Человекочитаемое время создания [`LogRecord`](https://python-all.ru/3.0/library/logging.html#logging.LogRecord). По умолчанию оно имеет вид “2003-07-08 16:49:45,896” (числа после запятой – миллисекунды времени). |908| `%(message)s` | Записанное сообщение. |909910Чтобы изменить формат даты/времени, можно передать дополнительный именованный параметр *datefmt*, как показано ниже:911912```python913import logging914915logging.basicConfig(level=logging.DEBUG,916                    format='%(asctime)s %(levelname)-8s %(message)s',917                    datefmt='%a, %d %b %Y %H:%M:%S',918                    filename='/temp/myapp.log',919                    filemode='w')920logging.debug('A debug message')921logging.info('Some information')922logging.warning('A shot across the bows')923```924925в результате чего вывод будет выглядеть как926927```python928Fri, 02 Jul 2004 13:06:18 DEBUG    A debug message929Fri, 02 Jul 2004 13:06:18 INFO     Some information930Fri, 02 Jul 2004 13:06:18 WARNING  A shot across the bows931```932933Строка формата даты соответствует требованиям `strftime()` – см. документацию модуля [`time`](https://python-all.ru/3.0/library/time.html#module-time).934935Если вместо отправки вывода журнала в консоль или файл вы хотите использовать объект, подобный файлу, созданный отдельно, вы можете передать его в [`basicConfig()`](https://python-all.ru/3.0/library/logging.html#logging.basicConfig) с помощью именованного аргумента *stream*. Обратите внимание, что если переданы оба именованных аргумента *stream* и *filename*, аргумент *stream* игнорируется.936937Конечно, вы можете включать переменную информацию в вывод. Для этого просто сделайте сообщение строкой формата и передайте дополнительные аргументы, содержащие переменную информацию, как в следующем примере:938939```python940import logging941942logging.basicConfig(level=logging.DEBUG,943                    format='%(asctime)s %(levelname)-8s %(message)s',944                    datefmt='%a, %d %b %Y %H:%M:%S',945                    filename='/temp/myapp.log',946                    filemode='w')947logging.error('Pack my box with %d dozen %s', 5, 'liquor jugs')948```949950в результате чего получится951952```python953Wed, 21 Jul 2004 15:35:16 ERROR    Pack my box with 5 dozen liquor jugs954```955956## Журналирование в несколько мест назначения957958Допустим, вы хотите вести журнал в консоль и файл с разными форматами сообщений и в разных ситуациях. Предположим, вы хотите записывать сообщения уровня DEBUG и выше в файл, а сообщения уровня INFO и выше – в консоль. Также предположим, что файл должен содержать временные метки, а консольные сообщения – нет. Вот как это можно сделать:959960```python961import logging962963# настроить логирование в файл – подробнее см. предыдущий раздел964logging.basicConfig(level=logging.DEBUG,965                    format='%(asctime)s %(name)-12s %(levelname)-8s %(message)s',966                    datefmt='%m-%d %H:%M',967                    filename='/temp/myapp.log',968                    filemode='w')969# Определяет обработчик, который записывает сообщения уровня INFO и выше в sys.stderr.970console = logging.StreamHandler()971console.setLevel(logging.INFO)972# Задает формат, упрощенный для вывода в консоль.973formatter = logging.Formatter('%(name)-12s: %(levelname)-8s %(message)s')974# Указывает обработчику использовать этот формат.975console.setFormatter(formatter)976# Добавляет обработчик в корневой логгер.977logging.getLogger('').addHandler(console)978979# Теперь можно выполнять запись в корневой логгер или любой другой. Сначала корневой...980logging.info('Jackdaws love my big sphinx of quartz.')981982# Теперь определите несколько других логгеров, которые могут представлять разные части приложения:983# приложение:984985logger1 = logging.getLogger('myapp.area1')986logger2 = logging.getLogger('myapp.area2')987988logger1.debug('Quick zephyrs blow, vexing daft Jim.')989logger1.info('How quickly daft jumping zebras vex.')990logger2.warning('Jail zesty vixen who grabbed pay from quack.')991logger2.error('The five boxing wizards jump quickly.')992```993994При запуске на консоли вы увидите995996```python997root        : INFO     Jackdaws love my big sphinx of quartz.998myapp.area1 : INFO     How quickly daft jumping zebras vex.999myapp.area2 : WARNING  Jail zesty vixen who grabbed pay from quack.1000myapp.area2 : ERROR    The five boxing wizards jump quickly.1001```10021003а в файле увидите примерно такое10041005```python100610-22 22:19 root         INFO     Jackdaws love my big sphinx of quartz.100710-22 22:19 myapp.area1  DEBUG    Quick zephyrs blow, vexing daft Jim.100810-22 22:19 myapp.area1  INFO     How quickly daft jumping zebras vex.100910-22 22:19 myapp.area2  WARNING  Jail zesty vixen who grabbed pay from quack.101010-22 22:19 myapp.area2  ERROR    The five boxing wizards jump quickly.1011```10121013Как видите, сообщение DEBUG отображается только в файле. Остальные сообщения отправляются в оба места назначения.10141015В этом примере используются консольный и файловый обработчики, но вы можете использовать любое количество и любое сочетание обработчиков по своему выбору.10161017## Добавление контекстной информации в вывод журнала10181019Иногда требуется, чтобы вывод логирования содержал контекстную информацию в дополнение к параметрам, переданным в вызов логирования. Например, в сетевом приложении может быть желательно регистрировать специфичную для клиента информацию в журнале (например, имя пользователя удаленного клиента или IP-адрес). Хотя вы можете использовать параметр *extra* для этого, не всегда удобно передавать информацию таким образом. Хотя может возникнуть соблазн создавать экземпляры `Logger` для каждого соединения, это не очень хорошая идея, потому что эти экземпляры не собираются сборщиком мусора. Хотя на практике это не проблема, когда количество экземпляров `Logger` зависит от уровня детализации, которую вы хотите использовать при логировании приложения, это может быть трудно управляемым, если количество экземпляров `Logger` становится фактически неограниченным.10201021Простой способ передавать контекстную информацию для вывода вместе с событием логирования – использовать класс [`LoggerAdapter`](https://python-all.ru/3.0/library/logging.html#logging.LoggerAdapter). Этот класс устроен так, что выглядит как `Logger`, поэтому можно вызывать [`debug()`](https://python-all.ru/3.0/library/logging.html#logging.debug), [`info()`](https://python-all.ru/3.0/library/logging.html#logging.info), [`warning()`](https://python-all.ru/3.0/library/logging.html#logging.warning), [`error()`](https://python-all.ru/3.0/library/logging.html#logging.error), [`exception()`](https://python-all.ru/3.0/library/logging.html#logging.exception), [`critical()`](https://python-all.ru/3.0/library/logging.html#logging.critical) и [`log()`](https://python-all.ru/3.0/library/logging.html#logging.log). Сигнатуры этих методов совпадают с их аналогами в `Logger`, так что экземпляры обоих типов можно использовать взаимозаменяемо.10221023При создании экземпляра [`LoggerAdapter`](https://python-all.ru/3.0/library/logging.html#logging.LoggerAdapter) ему передаются экземпляр `Logger` и объект, подобный словарю, содержащий контекстную информацию. При вызове любого из методов логирования у экземпляра [`LoggerAdapter`](https://python-all.ru/3.0/library/logging.html#logging.LoggerAdapter) он делегирует вызов нижележащему экземпляру `Logger`, переданному в его конструктор, и обеспечивает передачу контекстной информации в делегированном вызове. Вот фрагмент кода [`LoggerAdapter`](https://python-all.ru/3.0/library/logging.html#logging.LoggerAdapter):10241025```python1026def debug(self, msg, *args, **kwargs):1027    """1028    Передаёт вызов отладки нижележащему регистратору после добавления1029    контекстной информации из данного экземпляра адаптера.1030    """1031    msg, kwargs = self.process(msg, kwargs)1032    self.logger.debug(msg, *args, **kwargs)1033```10341035Метод `process()` класса [`LoggerAdapter`](https://python-all.ru/3.0/library/logging.html#logging.LoggerAdapter) – это место, где контекстная информация добавляется в вывод логирования. Ему передаются сообщение и именованные аргументы вызова логирования, и он возвращает (возможно) изменённые версии этих данных для использования в вызове нижележащего логгера. В реализации по умолчанию этот метод оставляет сообщение без изменений, но добавляет ключ «extra» в именованные аргументы, значением которого является объект, подобный словарю, переданный в конструктор. Разумеется, если в вызове адаптера уже был передан именованный аргумент «extra», он будет молча перезаписан.10361037Преимущество использования «extra» в том, что значения из объекта, подобного словарю, сливаются в [`LogRecord`](https://python-all.ru/3.0/library/logging.html#logging.LogRecord).\_\_dict\_\_, что позволяет использовать настроенные строки с экземплярами [`Formatter`](https://python-all.ru/3.0/library/logging.html#logging.Formatter), которым известны ключи этого объекта. Если нужен другой подход, например, требуется добавить контекстную информацию в начало или конец строки сообщения, достаточно создать подкласс [`LoggerAdapter`](https://python-all.ru/3.0/library/logging.html#logging.LoggerAdapter) и переопределить `process()`, чтобы он делал то, что нужно. Вот пример скрипта, использующего этот класс, который также демонстрирует, какое поведение, подобное словарю, требуется от произвольного «dict-like» объекта для использования в конструкторе:10381039```python1040import logging10411042class ConnInfo:1043    """1044    An example class which shows how an arbitrary class can be used as1045    the 'extra' context information repository passed to a LoggerAdapter.1046    """10471048    def __getitem__(self, name):1049        """1050        To allow this instance to look like a dict.1051        """1052        from random import choice1053        if name == "ip":1054            result = choice(["127.0.0.1", "192.168.0.1"])1055        elif name == "user":1056            result = choice(["jim", "fred", "sheila"])1057        else:1058            result = self.__dict__.get(name, "?")1059        return result10601061    def __iter__(self):1062        """1063        To allow iteration over keys, which will be merged into1064        the LogRecord dict before formatting and output.1065        """1066        keys = ["ip", "user"]1067        keys.extend(self.__dict__.keys())1068        return keys.__iter__()10691070if __name__ == "__main__":1071    from random import choice1072    levels = (logging.DEBUG, logging.INFO, logging.WARNING, logging.ERROR, logging.CRITICAL)1073    a1 = logging.LoggerAdapter(logging.getLogger("a.b.c"),1074                               { "ip" : "123.231.231.123", "user" : "sheila" })1075    logging.basicConfig(level=logging.DEBUG,1076                        format="%(asctime)-15s %(name)-5s %(levelname)-8s IP: %(ip)-15s User: %(user)-8s %(message)s")1077    a1.debug("A debug message")1078    a1.info("An info message with %s", "some parameters")1079    a2 = logging.LoggerAdapter(logging.getLogger("d.e.f"), ConnInfo())1080    for x in range(10):1081        lvl = choice(levels)1082        lvlname = logging.getLevelName(lvl)1083        a2.log(lvl, "A message at %s level with %d %s", lvlname, 2, "parameters")1084```10851086При запуске этого скрипта вывод должен выглядеть примерно так:10871088```python10892008-01-18 14:49:54,023 a.b.c DEBUG    IP: 123.231.231.123 User: sheila   A debug message10902008-01-18 14:49:54,023 a.b.c INFO     IP: 123.231.231.123 User: sheila   An info message with some parameters10912008-01-18 14:49:54,023 d.e.f CRITICAL IP: 192.168.0.1     User: jim      A message at CRITICAL level with 2 parameters10922008-01-18 14:49:54,033 d.e.f INFO     IP: 192.168.0.1     User: jim      A message at INFO level with 2 parameters10932008-01-18 14:49:54,033 d.e.f WARNING  IP: 192.168.0.1     User: sheila   A message at WARNING level with 2 parameters10942008-01-18 14:49:54,033 d.e.f ERROR    IP: 127.0.0.1       User: fred     A message at ERROR level with 2 parameters10952008-01-18 14:49:54,033 d.e.f ERROR    IP: 127.0.0.1       User: sheila   A message at ERROR level with 2 parameters10962008-01-18 14:49:54,033 d.e.f WARNING  IP: 192.168.0.1     User: sheila   A message at WARNING level with 2 parameters10972008-01-18 14:49:54,033 d.e.f WARNING  IP: 192.168.0.1     User: jim      A message at WARNING level with 2 parameters10982008-01-18 14:49:54,033 d.e.f INFO     IP: 192.168.0.1     User: fred     A message at INFO level with 2 parameters10992008-01-18 14:49:54,033 d.e.f WARNING  IP: 192.168.0.1     User: sheila   A message at WARNING level with 2 parameters11002008-01-18 14:49:54,033 d.e.f WARNING  IP: 127.0.0.1       User: jim      A message at WARNING level with 2 parameters1101```11021103## Отправка и получение событий логирования по сети11041105Допустим, требуется отправлять события логирования по сети и обрабатывать их на принимающей стороне. Простой способ – прикрепить экземпляр `SocketHandler` к корневому логгеру на отправляющей стороне:11061107```python1108import logging, logging.handlers11091110rootLogger = logging.getLogger('')1111rootLogger.setLevel(logging.DEBUG)1112socketHandler = logging.handlers.SocketHandler('localhost',1113                    logging.handlers.DEFAULT_TCP_LOGGING_PORT)1114# Не нужно использовать форматтер, так как сокет-обработчик отправляет событие в виде неформатированного пикла.1115# неформатированный pickle1116rootLogger.addHandler(socketHandler)11171118# Теперь можно выполнять запись в корневой логгер или любой другой. Сначала корневой...1119logging.info('Jackdaws love my big sphinx of quartz.')11201121# Теперь определите несколько других логгеров, которые могут представлять разные части приложения:1122# приложение:11231124logger1 = logging.getLogger('myapp.area1')1125logger2 = logging.getLogger('myapp.area2')11261127logger1.debug('Quick zephyrs blow, vexing daft Jim.')1128logger1.info('How quickly daft jumping zebras vex.')1129logger2.warning('Jail zesty vixen who grabbed pay from quack.')1130logger2.error('The five boxing wizards jump quickly.')1131```11321133На принимающей стороне можно настроить приемник с помощью модуля [`socketserver`](https://python-all.ru/3.0/library/socketserver.html#module-socketserver). Вот базовый рабочий пример:11341135```python1136import cPickle1137import logging1138import logging.handlers1139import socketserver1140import struct11411142class LogRecordStreamHandler(socketserver.StreamRequestHandler):1143    """Handler for a streaming logging request.11441145    This basically logs the record using whatever logging policy is1146    configured locally.1147    """11481149    def handle(self):1150        """1151        Handle multiple requests - each expected to be a 4-byte length,1152        followed by the LogRecord in pickle format. Logs the record1153        according to whatever policy is configured locally.1154        """1155        while True:1156            chunk = self.connection.recv(4)1157            if len(chunk) < 4:1158                break1159            slen = struct.unpack(">L", chunk)[0]1160            chunk = self.connection.recv(slen)1161            while len(chunk) < slen:1162                chunk = chunk + self.connection.recv(slen - len(chunk))1163            obj = self.unPickle(chunk)1164            record = logging.makeLogRecord(obj)1165            self.handleLogRecord(record)11661167    def unPickle(self, data):1168        return cPickle.loads(data)11691170    def handleLogRecord(self, record):1171        # if a name is specified, we use the named logger rather than the one1172        # implied by the record.1173        if self.server.logname is not None:1174            name = self.server.logname1175        else:1176            name = record.name1177        logger = logging.getLogger(name)1178        # N.B. EVERY record gets logged. This is because Logger.handle1179        # is normally called AFTER logger-level filtering. If you want1180        # to do filtering, do it at the client end to save wasting1181        # cycles and network bandwidth!1182        logger.handle(record)11831184class LogRecordSocketReceiver(socketserver.ThreadingTCPServer):1185    """simple TCP socket-based logging receiver suitable for testing.1186    """11871188    allow_reuse_address = 111891190    def __init__(self, host='localhost',1191                 port=logging.handlers.DEFAULT_TCP_LOGGING_PORT,1192                 handler=LogRecordStreamHandler):1193        socketserver.ThreadingTCPServer.__init__(self, (host, port), handler)1194        self.abort = 01195        self.timeout = 11196        self.logname = None11971198    def serve_until_stopped(self):1199        import select1200        abort = 01201        while not abort:1202            rd, wr, ex = select.select([self.socket.fileno()],1203                                       [], [],1204                                       self.timeout)1205            if rd:1206                self.handle_request()1207            abort = self.abort12081209def main():1210    logging.basicConfig(1211        format="%(relativeCreated)5d %(name)-15s %(levelname)-8s %(message)s")1212    tcpserver = LogRecordSocketReceiver()1213    print("About to start TCP server...")1214    tcpserver.serve_until_stopped()12151216if __name__ == "__main__":1217    main()1218```12191220Сначала запустите сервер, затем клиент. На стороне клиента в консоль ничего не выводится; на стороне сервера вы должны увидеть что-то вроде:12211222```python1223About to start TCP server...1224   59 root            INFO     Jackdaws love my big sphinx of quartz.1225   59 myapp.area1     DEBUG    Quick zephyrs blow, vexing daft Jim.1226   69 myapp.area1     INFO     How quickly daft jumping zebras vex.1227   69 myapp.area2     WARNING  Jail zesty vixen who grabbed pay from quack.1228   69 myapp.area2     ERROR    The five boxing wizards jump quickly.1229```12301231## Объекты-обработчики12321233Обработчики имеют следующие атрибуты и методы. Обратите внимание, что `Handler` никогда не создаётся напрямую; этот класс служит базой для более полезных подклассов. Однако метод [`__init__()`](https://python-all.ru/3.0/reference/datamodel.html#object.__init__) в подклассах должен вызывать [`Handler.__init__()`](https://python-all.ru/3.0/library/logging.html#logging.Handler.__init__).12341235#### `[logging.Handler.__init__]Handler.__init__(level=NOTSET)`12361237Инициализирует экземпляр12381239`Handler`12401241, устанавливая его уровень, устанавливая список фильтров в пустой список и создавая блокировку (с помощью12421243[`createLock()`](https://python-all.ru/3.0/library/logging.html#logging.Handler.createLock)12441245) для сериализации доступа к механизму ввода-вывода.12461247#### `[logging.Handler.createLock]Handler.createLock()`12481249Инициализирует блокировку потока, которую можно использовать для сериализации доступа к нижележащей функциональности ввода-вывода, которая может не быть потокобезопасной.12501251#### `[logging.Handler.acquire]Handler.acquire()`12521253Захватывает блокировку потока, созданную с помощью12541255[`createLock()`](https://python-all.ru/3.0/library/logging.html#logging.Handler.createLock)12561257.12581259#### `[logging.Handler.release]Handler.release()`12601261Освобождает блокировку потока, полученную с помощью12621263[`acquire()`](https://python-all.ru/3.0/library/logging.html#logging.Handler.acquire)12641265.12661267#### `[logging.Handler.setLevel]Handler.setLevel(lvl)`12681269Устанавливает порог для этого обработчика в12701271*lvl*12721273. Сообщения журнала, которые менее серьезны, чем12741275*lvl*12761277, будут игнорироваться. При создании обработчика уровень устанавливается в12781279`NOTSET`12801281(из-за чего обрабатываются все сообщения).12821283#### `[logging.Handler.setFormatter]Handler.setFormatter(form)`12841285Устанавливает12861287[`Formatter`](https://python-all.ru/3.0/library/logging.html#logging.Formatter)12881289для этого обработчика в12901291*form*12921293.12941295#### `[logging.Handler.addFilter]Handler.addFilter(filt)`12961297Добавляет указанный фильтр12981299*фильтр*13001301в данный обработчик.13021303#### `[logging.Handler.removeFilter]Handler.removeFilter(filt)`13041305Удаляет указанный фильтр13061307*фильтр*13081309из данного обработчика.13101311#### `[logging.Handler.filter]Handler.filter(record)`13121313Применяет фильтры этого обработчика к записи и возвращает true, если запись должна быть обработана.13141315#### `[logging.Handler.flush]Handler.flush()`13161317Обеспечивает сброс всего вывода логирования. Данная версия ничего не делает и предназначена для реализации в подклассах.13181319#### `[logging.Handler.close]Handler.close()`13201321Освобождает все ресурсы, используемые обработчиком. Эта версия не производит вывод, но удаляет обработчик из внутреннего списка обработчиков, который закрывается при вызове13221323[`shutdown()`](https://python-all.ru/3.0/library/logging.html#logging.shutdown)13241325. Подклассы должны гарантировать, что этот метод вызывается из переопределенных методов13261327[`close()`](https://python-all.ru/3.0/library/logging.html#logging.Handler.close)13281329.13301331#### `[logging.Handler.handle]Handler.handle(record)`13321333Условно выдает указанную запись логирования в зависимости от фильтров, которые могут быть добавлены к обработчику. Оборачивает фактическую выдачу записи в захват/освобождение блокировки потока ввода-вывода.13341335#### `[logging.Handler.handleError]Handler.handleError(record)`13361337Этот метод следует вызывать из обработчиков при возникновении исключения во время вызова13381339[`emit()`](https://python-all.ru/3.0/library/logging.html#logging.Handler.emit)13401341. По умолчанию он ничего не делает, то есть исключения молча игнорируются. В системе логирования это в основном то, что нужно – большинство пользователей не интересуются ошибками в системе логирования, их больше волнуют ошибки приложения. Однако при желании можно заменить этот метод собственным обработчиком. Указанная запись (record) – это та, которая обрабатывалась в момент возникновения исключения.13421343#### `[logging.Handler.format]Handler.format(record)`13441345Форматирует запись – если задан форматировщик, использует его. В противном случае использует форматировщик по умолчанию для модуля.13461347#### `[logging.Handler.emit]Handler.emit(record)`13481349Делает все необходимое для фактической записи указанной записи журнала. Эта версия предназначена для реализации в подклассах и поэтому вызывает13501351[`NotImplementedError`](https://python-all.ru/3.0/library/exceptions.html#exceptions.NotImplementedError)13521353.13541355### StreamHandler13561357Класс [`StreamHandler`](https://python-all.ru/3.0/library/logging.html#logging.StreamHandler), находящийся в основном пакете `logging`, направляет вывод логирования в потоки, такие как *sys.stdout*, *sys.stderr* или любой объект, похожий на файл (точнее, любой объект, поддерживающий методы `write()` и `flush()`).13581359#### `[logging.StreamHandler]class logging.StreamHandler([strm])`13601361Возвращает новый экземпляр класса [`StreamHandler`](https://python-all.ru/3.0/library/logging.html#logging.StreamHandler). Если указан *strm*, экземпляр будет использовать его для вывода журнала; в противном случае будет использоваться *sys.stderr*.13621363#### `[logging.StreamHandler.emit]emit(record)`13641365Если задан форматтер, он используется для форматирования записи. Затем запись записывается в поток с завершающим символом новой строки. Если присутствует информация об исключении, она форматируется с помощью13661367[`traceback.print_exception()`](https://python-all.ru/3.0/library/traceback.html#traceback.print_exception)13681369и добавляется в поток.13701371#### `[logging.StreamHandler.flush]flush()`13721373Сбрасывает поток данных, вызывая его метод13741375[`flush()`](https://python-all.ru/3.0/library/logging.html#logging.StreamHandler.flush)13761377. Обратите внимание, что метод13781379`close()`13801381наследуется от13821383`Handler`13841385и поэтому не выводит ничего, так что иногда может потребоваться явный вызов13861387[`flush()`](https://python-all.ru/3.0/library/logging.html#logging.StreamHandler.flush)13881389.13901391### FileHandler13921393Класс [`FileHandler`](https://python-all.ru/3.0/library/logging.html#logging.FileHandler), находящийся в основном пакете `logging`, направляет вывод логирования в файл на диске. Он наследует функциональность вывода от [`StreamHandler`](https://python-all.ru/3.0/library/logging.html#logging.StreamHandler).13941395#### `[logging.FileHandler]class logging.FileHandler(filename[, mode[, encoding[, delay]]])`13961397Возвращает новый экземпляр класса [`FileHandler`](https://python-all.ru/3.0/library/logging.html#logging.FileHandler). Указанный файл открывается и используется как поток для логирования. Если *mode* не указан, используется `'a'`. Если *encoding* не равен *None*, файл открывается с указанной кодировкой. Если *delay* равен true, открытие файла откладывается до первого вызова [`emit()`](https://python-all.ru/3.0/library/logging.html#logging.FileHandler.emit). По умолчанию файл растёт бесконечно.13981399#### `[logging.FileHandler.close]close()`14001401Закрывает файл.14021403#### `[logging.FileHandler.emit]emit(record)`14041405Выводит запись в файл.14061407### NullHandler14081409Новое в версии 3.1.14101411Класс [`NullHandler`](https://python-all.ru/3.0/library/logging.html#logging.NullHandler), находящийся в основном пакете `logging`, не выполняет никакого форматирования или вывода. По сути, это обработчик-пустышка, предназначенный для разработчиков библиотек.14121413#### `[logging.NullHandler]class logging.NullHandler`14141415Возвращает новый экземпляр класса [`NullHandler`](https://python-all.ru/3.0/library/logging.html#logging.NullHandler).14161417#### `[logging.NullHandler.emit]emit(record)`14181419Этот метод ничего не делает.14201421Смотрите [*Настройка логирования для библиотеки*](https://python-all.ru/3.0/library/logging.html#library-config) для получения дополнительной информации об использовании [`NullHandler`](https://python-all.ru/3.0/library/logging.html#logging.NullHandler).14221423### WatchedFileHandler14241425Класс [`WatchedFileHandler`](https://python-all.ru/3.0/library/logging.html#logging.handlers.WatchedFileHandler), находящийся в модуле `logging.handlers`, представляет собой `FileHandler`, который следит за файлом, в который ведётся логирование. Если файл изменяется, он закрывается и открывается заново по тому же имени файла.14261427Файл может измениться из-за использования таких программ, как *newsyslog* и *logrotate*, которые выполняют ротацию файлов журнала. Этот обработчик, предназначенный для использования в Unix/Linux, следит, не изменился ли файл с момента последнего вызова emit. (Считается, что файл изменился, если изменились его устройство или inode.) Если файл изменился, старый поток файла закрывается, а файл открывается для получения нового потока.14281429Этот обработчик не подходит для использования в Windows, поскольку в Windows открытые файлы журналов нельзя перемещать или переименовывать – система логирования открывает файлы с эксклюзивными блокировками, – поэтому в таком обработчике нет необходимости. Кроме того, *ST\_INO* не поддерживается в Windows; `stat()` всегда возвращает ноль для этого значения.14301431#### `[logging.handlers.WatchedFileHandler]class logging.handlers.WatchedFileHandler(filename[, mode[, encoding[, delay]]])`14321433Возвращает новый экземпляр класса [`WatchedFileHandler`](https://python-all.ru/3.0/library/logging.html#logging.handlers.WatchedFileHandler). Указанный файл открывается и используется как поток для логирования. Если *mode* не указан, используется `'a'`. Если *encoding* не равен *None*, файл открывается с указанной кодировкой. Если *delay* равен true, открытие файла откладывается до первого вызова [`emit()`](https://python-all.ru/3.0/library/logging.html#logging.handlers.WatchedFileHandler.emit). По умолчанию файл растёт бесконечно.14341435#### `[logging.handlers.WatchedFileHandler.emit]emit(record)`14361437Выводит запись в файл, но сначала проверяет, изменился ли файл. Если изменился, существующий поток сбрасывается и закрывается, а файл открывается заново, после чего запись выводится в файл.14381439### RotatingFileHandler14401441Класс [`RotatingFileHandler`](https://python-all.ru/3.0/library/logging.html#logging.handlers.RotatingFileHandler), находящийся в модуле `logging.handlers`, поддерживает ротацию файлов журналов на диске.14421443#### `[logging.handlers.RotatingFileHandler]class logging.handlers.RotatingFileHandler(filename[, mode[, maxBytes[, backupCount[, encoding[, delay]]]]])`14441445Возвращает новый экземпляр класса [`RotatingFileHandler`](https://python-all.ru/3.0/library/logging.html#logging.handlers.RotatingFileHandler). Указанный файл открывается и используется как поток для журналирования. Если *mode* не указан, используется `'a'`. Если *encoding* не равен *None*, файл открывается с указанной кодировкой. Если *delay* равен истине, открытие файла откладывается до первого вызова [`emit()`](https://python-all.ru/3.0/library/logging.html#logging.handlers.RotatingFileHandler.emit). По умолчанию файл растёт бесконечно.14461447С помощью параметров *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` и так далее.14481449#### `[logging.handlers.RotatingFileHandler.doRollover]doRollover()`14501451Выполняет ротацию, как описано выше.14521453#### `[logging.handlers.RotatingFileHandler.emit]emit(record)`14541455Записывает запись (record) в файл, выполняя при необходимости ротацию, как описано ранее.14561457### TimedRotatingFileHandler14581459Класс [`TimedRotatingFileHandler`](https://python-all.ru/3.0/library/logging.html#logging.handlers.TimedRotatingFileHandler), находящийся в модуле `logging.handlers`, поддерживает ротацию файлов журналов на диске через заданные промежутки времени.14601461#### `[logging.handlers.TimedRotatingFileHandler]class logging.handlers.TimedRotatingFileHandler(filename[, when[, interval[, backupCount[, encoding[, delay[, utc]]]]]])`14621463Возвращает новый экземпляр класса [`TimedRotatingFileHandler`](https://python-all.ru/3.0/library/logging.html#logging.handlers.TimedRotatingFileHandler). Указанный файл открывается и используется как поток для журналирования. При ротации также устанавливается суффикс имени файла. Ротация происходит на основе произведения *when* и *interval*.14641465Параметр *when* указывает тип *interval*. Список возможных значений приведён ниже. Обратите внимание, что регистр не учитывается.14661467| Значение | Тип интервала |1468| --- | --- |1469| `'S'` | Секунды |1470| `'M'` | Минуты |1471| `'H'` | Часы |1472| `'D'` | Дни |1473| `'W'` | День недели (0 = понедельник) |1474| `'midnight'` | Переключение в полночь |14751476Система будет сохранять старые файлы журнала, добавляя расширения к имени файла. Расширения основаны на дате и времени, с использованием формата strftime `%Y-%m-%d_%H-%M-%S` или его начальной части, в зависимости от интервала смены файлов. Если аргумент *utc* равен истине, будут использоваться времена в UTC; в противном случае используется местное время.14771478Если *backupCount* не равен нулю, сохраняется не более *backupCount* файлов; если при ротации должно быть создано больше файлов, самый старый удаляется. Логика удаления использует интервал для определения того, какие файлы нужно удалить, поэтому изменение интервала может привести к тому, что старые файлы останутся.14791480#### `[logging.handlers.TimedRotatingFileHandler.doRollover]doRollover()`14811482Выполняет ротацию, как описано выше.14831484#### `[logging.handlers.TimedRotatingFileHandler.emit]emit(record)`14851486Выводит запись в файл, обеспечивая ротацию, как описано выше.14871488### SocketHandler14891490Класс [`SocketHandler`](https://python-all.ru/3.0/library/logging.html#logging.handlers.SocketHandler), расположенный в модуле `logging.handlers`, отправляет вывод журнала на сетевой сокет. Базовый класс использует TCP-сокет.14911492#### `[logging.handlers.SocketHandler]class logging.handlers.SocketHandler(host, port)`14931494Возвращает новый экземпляр класса [`SocketHandler`](https://python-all.ru/3.0/library/logging.html#logging.handlers.SocketHandler), предназначенного для взаимодействия с удаленной машиной, адрес которой задается *host* и *port*.14951496#### `[logging.handlers.SocketHandler.close]close()`14971498Закрывает сокет.14991500#### `[logging.handlers.SocketHandler.emit]emit()`15011502Сериализует словарь атрибутов записи и записывает его в сокет в бинарном формате. Если при работе с сокетом возникает ошибка, пакет молча отбрасывается. Если соединение было ранее потеряно, оно восстанавливается. Для десериализации записи на принимающей стороне в15031504`LogRecord`15051506используйте функцию15071508`makeLogRecord()`15091510.15111512#### `[logging.handlers.SocketHandler.handleError]handleError()`15131514Обрабатывает ошибку, возникшую во время15151516[`emit()`](https://python-all.ru/3.0/library/logging.html#logging.handlers.SocketHandler.emit)15171518. Наиболее вероятная причина – потеря соединения. Закрывает сокет, чтобы можно было повторить попытку при следующем событии.15191520#### `[logging.handlers.SocketHandler.makeSocket]makeSocket()`15211522Это фабричный метод, позволяющий подклассам определять точный тип сокета, который они хотят. Реализация по умолчанию создает TCP-сокет (15231524[`socket.SOCK_STREAM`](https://python-all.ru/3.0/library/socket.html#socket.SOCK_STREAM)15251526).15271528#### `[logging.handlers.SocketHandler.makePickle]makePickle(record)`15291530Сериализует словарь атрибутов записи в двоичном формате с префиксом длины и возвращает его готовым к передаче через сокет.15311532#### `[logging.handlers.SocketHandler.send]send(packet)`15331534Отправляет сериализованную с помощью pickle строку15351536*packet*15371538в сокет. Эта функция допускает частичную отправку, которая может произойти, когда сеть занята.15391540### DatagramHandler15411542Класс [`DatagramHandler`](https://python-all.ru/3.0/library/logging.html#logging.handlers.DatagramHandler), расположенный в модуле `logging.handlers` , наследуется от [`SocketHandler`](https://python-all.ru/3.0/library/logging.html#logging.handlers.SocketHandler) для поддержки отправки сообщений журнала через UDP-сокеты.15431544#### `[logging.handlers.DatagramHandler]class logging.handlers.DatagramHandler(host, port)`15451546Возвращает новый экземпляр класса [`DatagramHandler`](https://python-all.ru/3.0/library/logging.html#logging.handlers.DatagramHandler), предназначенного для взаимодействия с удаленной машиной, адрес которой задается *host* и *port*.15471548#### `[logging.handlers.DatagramHandler.emit]emit()`15491550Сериализует словарь атрибутов записи и записывает его в сокет в бинарном формате. Если при работе с сокетом возникает ошибка, пакет молча отбрасывается. Для десериализации записи на принимающей стороне в15511552`LogRecord`15531554используйте функцию15551556`makeLogRecord()`15571558.15591560#### `[logging.handlers.DatagramHandler.makeSocket]makeSocket()`15611562Фабричный метод15631564[`SocketHandler`](https://python-all.ru/3.0/library/logging.html#logging.handlers.SocketHandler)15651566здесь переопределен для создания UDP-сокета (15671568[`socket.SOCK_DGRAM`](https://python-all.ru/3.0/library/socket.html#socket.SOCK_DGRAM)15691570).15711572#### `[logging.handlers.DatagramHandler.send]send(s)`15731574Отправляет сериализованную с помощью pickle строку в сокет.15751576### SysLogHandler15771578Класс [`SysLogHandler`](https://python-all.ru/3.0/library/logging.html#logging.handlers.SysLogHandler), расположенный в модуле `logging.handlers`, поддерживает отправку сообщений журнала в удаленный или локальный системный журнал Unix.15791580#### `[logging.handlers.SysLogHandler]class logging.handlers.SysLogHandler([address[, facility]])`15811582Возвращает новый экземпляр класса [`SysLogHandler`](https://python-all.ru/3.0/library/logging.html#logging.handlers.SysLogHandler), предназначенный для связи с удаленной Unix-машиной, адрес которой задается параметром *address* в виде кортежа `(host, port)`. Если *address* не указан, используется `('localhost', 514)`. Адрес используется для открытия UDP-сокета. Альтернативой кортежу `(host, port)` является указание адреса в виде строки, например “/dev/log”. В этом случае для отправки сообщения в syslog используется сокет домена Unix. Если *facility* не указан, используется `LOG_USER`.15831584#### `[logging.handlers.SysLogHandler.close]close()`15851586Закрывает сокет, подключённый к удалённому узлу.15871588#### `[logging.handlers.SysLogHandler.emit]emit(record)`15891590Запись форматируется и отправляется на сервер syslog. Если присутствует информация об исключении, она15911592*не*15931594отправляется на сервер.15951596#### `[logging.handlers.SysLogHandler.encodePriority]encodePriority(facility, priority)`15971598Кодирует средство и приоритет в целое число. Можно передавать строки или целые числа – если передаются строки, внутренние словари сопоставлений используются для преобразования их в целые числа.15991600### NTEventLogHandler16011602Класс [`NTEventLogHandler`](https://python-all.ru/3.0/library/logging.html#logging.handlers.NTEventLogHandler), находящийся в модуле `logging.handlers`, поддерживает отправку сообщений журналирования в журнал событий локальной Windows NT, Windows 2000 или Windows XP. Перед использованием необходимо установить расширения Win32 от Марка Хэммонда для Python.16031604#### `[logging.handlers.NTEventLogHandler]class logging.handlers.NTEventLogHandler(appname[, dllname[, logtype]])`16051606Возвращает новый экземпляр класса [`NTEventLogHandler`](https://python-all.ru/3.0/library/logging.html#logging.handlers.NTEventLogHandler). Параметр *appname* используется для определения имени приложения, отображаемого в журнале событий. С использованием этого имени создается соответствующая запись реестра. Параметр *dllname* должен содержать полный путь к .dll или .exe-файлу с определениями сообщений для хранения в журнале (если не указан, используется `'win32service.pyd'` – он устанавливается вместе с расширениями Win32 и содержит несколько базовых определений сообщений-заполнителей. Обратите внимание, что использование этих заполнителей приведет к увеличению размера журнала, так как весь источник сообщений хранится в журнале. Если нужны более компактные журналы, следует передать имя собственного .dll или .exe, содержащего нужные определения сообщений). Параметр *logtype* может быть одним из `'Application'`, `'System'` или `'Security'`, по умолчанию `'Application'`.16071608#### `[logging.handlers.NTEventLogHandler.close]close()`16091610На этом этапе можно удалить имя приложения из реестра как источник записей журнала событий. Однако после этого события не будут отображаться в средстве просмотра журнала событий так, как предполагалось – для получения имени .dll требуется доступ к реестру. Текущая версия этого не делает.16111612#### `[logging.handlers.NTEventLogHandler.emit]emit(record)`16131614Определяет идентификатор сообщения, категорию и тип события, а затем записывает сообщение в журнал событий NT.16151616#### `[logging.handlers.NTEventLogHandler.getEventCategory]getEventCategory(record)`16171618Возвращает категорию события для записи. Переопределите этот метод, чтобы указать собственные категории. Данная версия возвращает 0.16191620#### `[logging.handlers.NTEventLogHandler.getEventType]getEventType(record)`16211622Возвращает тип события для записи. Переопределите этот метод, если нужно задать собственные типы. В данной версии используется сопоставление через атрибут typemap обработчика, который устанавливается в16231624[`__init__()`](https://python-all.ru/3.0/reference/datamodel.html#object.__init__)16251626в виде словаря, содержащего сопоставления для16271628`DEBUG`16291630,16311632`INFO`16331634,16351636`WARNING`16371638,16391640`ERROR`16411642и16431644`CRITICAL`16451646. При использовании собственных уровней потребуется либо переопределить этот метод, либо поместить подходящий словарь в атрибут16471648*typemap*16491650обработчика.16511652#### `[logging.handlers.NTEventLogHandler.getMessageID]getMessageID(record)`16531654Возвращает идентификатор сообщения для записи. При использовании собственных сообщений можно передать в логгер идентификатор16551656*msg*16571658вместо строки форматирования. Затем здесь можно выполнить поиск по словарю для получения идентификатора сообщения. Данная версия возвращает 1 – базовый идентификатор сообщения в16591660`win32service.pyd`16611662.16631664### SMTPHandler16651666Класс [`SMTPHandler`](https://python-all.ru/3.0/library/logging.html#logging.handlers.SMTPHandler), находящийся в модуле `logging.handlers`, поддерживает отправку сообщений журналирования на адрес электронной почты через SMTP.16671668#### `[logging.handlers.SMTPHandler]class logging.handlers.SMTPHandler(mailhost, fromaddr, toaddrs, subject[, credentials])`16691670Возвращает новый экземпляр класса [`SMTPHandler`](https://python-all.ru/3.0/library/logging.html#logging.handlers.SMTPHandler). Экземпляр инициализируется адресами отправителя, получателя и темой письма. Параметр *toaddrs* должен быть списком строк. Для указания нестандартного порта SMTP используется кортеж (host, port) в аргументе *mailhost*. Если передана строка, используется стандартный порт SMTP. Если SMTP-сервер требует аутентификации, можно указать кортеж (username, password) в аргументе *credentials*.16711672#### `[logging.handlers.SMTPHandler.emit]emit(record)`16731674Форматирует запись и отправляет её указанным получателям.16751676#### `[logging.handlers.SMTPHandler.getSubject]getSubject(record)`16771678Чтобы задать тему письма, зависящую от записи, переопределите этот метод.16791680### MemoryHandler16811682Класс [`MemoryHandler`](https://python-all.ru/3.0/library/logging.html#logging.handlers.MemoryHandler), находящийся в модуле `logging.handlers`, поддерживает буферизацию записей журналирования в памяти с периодической отправкой их в обработчик *target*. Сброс происходит, когда буфер полон или когда появляется событие определённой важности или выше.16831684[`MemoryHandler`](https://python-all.ru/3.0/library/logging.html#logging.handlers.MemoryHandler) является подклассом более общего [`BufferingHandler`](https://python-all.ru/3.0/library/logging.html#logging.handlers.BufferingHandler), который является абстрактным классом. Он буферизирует записи журналирования в памяти. Каждый раз, когда запись добавляется в буфер, вызывается `shouldFlush()` для проверки необходимости сброса буфера. Если сброс нужен, то `flush()` должен выполнить необходимые действия.16851686#### `[logging.handlers.BufferingHandler]class logging.handlers.BufferingHandler(capacity)`16871688Инициализирует обработчик буфером указанной ёмкости.16891690#### `[logging.handlers.BufferingHandler.emit]emit(record)`16911692Добавляет запись в буфер. Если16931694[`shouldFlush()`](https://python-all.ru/3.0/library/logging.html#logging.handlers.BufferingHandler.shouldFlush)16951696возвращает true, вызывает16971698[`flush()`](https://python-all.ru/3.0/library/logging.html#logging.handlers.BufferingHandler.flush)16991700для обработки буфера.17011702#### `[logging.handlers.BufferingHandler.flush]flush()`17031704Вы можете переопределить этот метод, чтобы задать собственное поведение при сбросе. Текущая версия просто очищает буфер.17051706#### `[logging.handlers.BufferingHandler.shouldFlush]shouldFlush(record)`17071708Возвращает true, если буфер заполнен до ёмкости. Этот метод можно переопределить для реализации собственных стратегий сброса.17091710#### `[logging.handlers.MemoryHandler]class logging.handlers.MemoryHandler(capacity[, flushLevel[, target]])`17111712Возвращает новый экземпляр класса [`MemoryHandler`](https://python-all.ru/3.0/library/logging.html#logging.handlers.MemoryHandler). Экземпляр инициализируется с размером буфера *capacity*. Если *flushLevel* не указан, используется `ERROR`. Если *target* не указан, перед тем как обработчик начнёт работать, цель нужно задать с помощью [`setTarget()`](https://python-all.ru/3.0/library/logging.html#logging.handlers.MemoryHandler.setTarget).17131714#### `[logging.handlers.MemoryHandler.close]close()`17151716Вызывает17171718[`flush()`](https://python-all.ru/3.0/library/logging.html#logging.handlers.MemoryHandler.flush)17191720, устанавливает цель в17211722[`None`](https://python-all.ru/3.0/library/constants.html#None)17231724и очищает буфер.17251726#### `[logging.handlers.MemoryHandler.flush]flush()`17271728Для17291730[`MemoryHandler`](https://python-all.ru/3.0/library/logging.html#logging.handlers.MemoryHandler)17311732сброс означает просто отправку буферизированных записей целевому обработчику, если он есть. Переопределите, если требуется иное поведение.17331734#### `[logging.handlers.MemoryHandler.setTarget]setTarget(target)`17351736Устанавливает целевой обработчик для данного обработчика.17371738#### `[logging.handlers.MemoryHandler.shouldFlush]shouldFlush(record)`17391740Проверяет, заполнен ли буфер или поступила запись уровня17411742*flushLevel*17431744или выше.17451746### HTTPHandler17471748Класс [`HTTPHandler`](https://python-all.ru/3.0/library/logging.html#logging.handlers.HTTPHandler), расположенный в модуле `logging.handlers`, поддерживает отправку сообщений журнала на веб-сервер, используя семантику `GET` или `POST`.17491750#### `[logging.handlers.HTTPHandler]class logging.handlers.HTTPHandler(host, url[, method])`17511752Возвращает новый экземпляр класса [`HTTPHandler`](https://python-all.ru/3.0/library/logging.html#logging.handlers.HTTPHandler). Экземпляр инициализируется адресом хоста, URL и HTTP-методом. *host* может быть в формате `host:port`, если требуется указать определённый номер порта. Если *method* не указан, используется `GET`.17531754#### `[logging.handlers.HTTPHandler.emit]emit(record)`17551756Отправляет запись на веб-сервер в виде словаря, закодированного в URL.17571758## Объекты Formatter17591760[`Formatter`](https://python-all.ru/3.0/library/logging.html#logging.Formatter) имеют следующие атрибуты и методы. Они отвечают за преобразование [`LogRecord`](https://python-all.ru/3.0/library/logging.html#logging.LogRecord) в (обычно) строку, которая может интерпретироваться человеком или внешней системой. Базовый [`Formatter`](https://python-all.ru/3.0/library/logging.html#logging.Formatter) позволяет задать строку форматирования. Если она не указана, используется значение по умолчанию `'%(message)s'`.17611762Formatter можно инициализировать строкой форматирования, которая использует знание атрибутов [`LogRecord`](https://python-all.ru/3.0/library/logging.html#logging.LogRecord) – например, упомянутое выше значение по умолчанию, использующее тот факт, что сообщение и аргументы пользователя уже предварительно отформатированы в атрибут *message* объекта [`LogRecord`](https://python-all.ru/3.0/library/logging.html#logging.LogRecord). Эта строка форматирования содержит стандартные ключи отображения в стиле % языка Python. Дополнительные сведения о форматировании строк см. в разделе [*Old String Formatting Operations*](https://python-all.ru/3.0/library/stdtypes.html#old-string-formatting).17631764Полезные ключи отображения в [`LogRecord`](https://python-all.ru/3.0/library/logging.html#logging.LogRecord) на данный момент:17651766| Формат | Описание |1767| --- | --- |1768| `%(name)s` | Имя логгера (канала журналирования). |1769| `%(levelno)s` | Числовой уровень логирования сообщения (`DEBUG`, `INFO`, `WARNING`, `ERROR`, `CRITICAL`). |1770| `%(levelname)s` | Текстовый уровень логирования сообщения (`'DEBUG'`, `'INFO'`, `'WARNING'`, `'ERROR'`, `'CRITICAL'`). |1771| `%(pathname)s` | Полный путь к исходному файлу, из которого был сделан вызов логирования (если доступен). |1772| `%(filename)s` | Имя файла из пути. |1773| `%(module)s` | Модуль (часть имени файла). |1774| `%(funcName)s` | Имя функции, содержащей вызов логирования. |1775| `%(lineno)d` | Номер строки исходного кода, из которой был сделан вызов логирования (если доступен). |1776| `%(created)f` | Время создания [`LogRecord`](https://python-all.ru/3.0/library/logging.html#logging.LogRecord) (возвращаемое [`time.time()`](https://python-all.ru/3.0/library/time.html#time.time)). |1777| `%(relativeCreated)d` | Время в миллисекундах, когда LogRecord был создан, относительно времени загрузки модуля logging. |1778| `%(asctime)s` | Человекочитаемое время создания [`LogRecord`](https://python-all.ru/3.0/library/logging.html#logging.LogRecord). По умолчанию оно имеет вид “2003-07-08 16:49:45,896” (числа после запятой – миллисекунды времени). |1779| `%(msecs)d` | Миллисекундная часть времени, когда был создан [`LogRecord`](https://python-all.ru/3.0/library/logging.html#logging.LogRecord). |1780| `%(поток)d` | Идентификатор потока (если доступен). |1781| `%(threadName)s` | Имя потока (если доступно). |1782| `%(процесс)d` | Идентификатор процесса (если доступен). |1783| `%(message)s` | Записанное сообщение, вычисляемое как `msg % args`. |17841785#### `[logging.Formatter]class logging.Formatter([fmt[, datefmt]])`17861787Возвращает новый экземпляр класса [`Formatter`](https://python-all.ru/3.0/library/logging.html#logging.Formatter). Экземпляр инициализируется строкой форматирования для всего сообщения, а также строкой форматирования для части даты/времени сообщения. Если *fmt* не указан, используется `'%(message)s'`. Если *datefmt* не указан, используется формат даты ISO8601.17881789#### `[logging.Formatter.format]format(record)`17901791Словарь атрибутов записи используется как операнд для операции форматирования строки. Возвращает результирующую строку. Перед форматированием словаря выполняется несколько подготовительных шагов. Атрибут17921793*message*17941795записи вычисляется с помощью17961797*msg*17981799%18001801*args*18021803. Если строка форматирования содержит18041805`'(asctime)'`18061807, вызывается18081809[`formatTime()`](https://python-all.ru/3.0/library/logging.html#logging.Formatter.formatTime)18101811для форматирования времени события. Если присутствует информация об исключении, она форматируется с помощью18121813[`formatException()`](https://python-all.ru/3.0/library/logging.html#logging.Formatter.formatException)18141815и добавляется к сообщению. Обратите внимание, что отформатированная информация об исключении кэшируется в атрибуте18161817*exc\_text*18181819. Это полезно, поскольку информацию об исключении можно сериализовать и отправить по сети, но следует быть осторожным, если имеется более одного подкласса18201821[`Formatter`](https://python-all.ru/3.0/library/logging.html#logging.Formatter)18221823, который настраивает форматирование информации об исключении. В этом случае вам придется очищать кэшированное значение после того, как форматировщик выполнит свое форматирование, чтобы следующий форматировщик, обрабатывающий событие, не использовал кэшированное значение, а пересчитал его заново.18241825#### `[logging.Formatter.formatTime]formatTime(record[, datefmt])`18261827Этот метод должен вызываться из18281829[`format()`](https://python-all.ru/3.0/library/functions.html#format)18301831форматировщиком, который хочет использовать отформатированное время. Этот метод может быть переопределен в форматировщиках для любых специфических требований, но базовое поведение следующее: если указан18321833*datefmt*18341835(строка), он используется с18361837[`time.strftime()`](https://python-all.ru/3.0/library/time.html#time.strftime)18381839для форматирования времени создания записи. В противном случае используется формат ISO8601. Возвращается результирующая строка.18401841#### `[logging.Formatter.formatException]formatException(exc_info)`18421843Форматирует указанную информацию об исключении (стандартный кортеж исключения, возвращаемый18441845[`sys.exc_info()`](https://python-all.ru/3.0/library/sys.html#sys.exc_info)18461847) в виде строки. Эта реализация по умолчанию просто использует18481849[`traceback.print_exception()`](https://python-all.ru/3.0/library/traceback.html#traceback.print_exception)18501851. Полученная строка возвращается.18521853## Объекты фильтров18541855[`Filter`](https://python-all.ru/3.0/library/logging.html#logging.Filter) могут использоваться обработчиками `Handler` и регистраторами `Logger` для более тонкой фильтрации, чем предусмотрено уровнями. Базовый класс фильтра допускает только события, которые находятся ниже определённой точки в иерархии регистраторов. Например, фильтр, инициализированный строкой “A.B”, будет пропускать события, зарегистрированные регистраторами “A.B”, “A.B.C”, “A.B.C.D”, “A.B.D” и т.д., но не “A.BB”, “B.A.B” и т.д. Если инициализирован пустой строкой, пропускаются все события.18561857#### `[logging.Filter]class logging.Filter([name])`18581859Возвращает экземпляр класса [`Filter`](https://python-all.ru/3.0/library/logging.html#logging.Filter). Если указан *name*, он задаёт имя регистратора, события которого (вместе с событиями его дочерних регистраторов) будут пропускаться через фильтр. Если имя не указано, пропускаются все события.18601861#### `[logging.Filter.filter]filter(record)`18621863Следует ли регистрировать указанную запись? Возвращает ноль, если нет, и ненулевое значение, если да. Если это уместно, запись может быть изменена на месте данным методом.18641865## Объекты LogRecord18661867Экземпляры [`LogRecord`](https://python-all.ru/3.0/library/logging.html#logging.LogRecord) создаются каждый раз, когда что-либо регистрируется. Они содержат всю информацию, относящуюся к регистрируемому событию. Основная передаваемая информация находится в msg и args, которые объединяются с помощью msg % args для создания поля message записи. Запись также включает информацию, такую как время создания записи, исходную строку, в которой был выполнен вызов регистрации, и любую информацию об исключении, подлежащую регистрации.18681869#### `[logging.LogRecord]class logging.LogRecord(name, lvl, pathname, lineno, msg, args, exc_info[, func])`18701871Возвращает экземпляр [`LogRecord`](https://python-all.ru/3.0/library/logging.html#logging.LogRecord), инициализированный информацией. *name* – имя регистратора; *lvl* – числовой уровень; *pathname* – абсолютный путь к исходному файлу, в котором был сделан вызов логирования; *lineno* – номер строки в этом файле, где находится вызов логирования; *msg* – сообщение, предоставленное пользователем (строка формата); *args* – кортеж, который вместе с *msg* составляет пользовательское сообщение; *exc\_info* – кортеж исключения, полученный вызовом [`sys.exc_info()`](https://python-all.ru/3.0/library/sys.html#sys.exc_info) (или [`None`](https://python-all.ru/3.0/library/constants.html#None), если информация об исключении недоступна). *func* – имя функции, из которой был сделан вызов логирования. Если не указано, по умолчанию `None`.18721873#### `[logging.LogRecord.getMessage]getMessage()`18741875Возвращает сообщение для данного экземпляра18761877[`LogRecord`](https://python-all.ru/3.0/library/logging.html#logging.LogRecord)18781879после объединения любых предоставленных пользователем аргументов с сообщением.18801881## Объекты LoggerAdapter18821883Экземпляры [`LoggerAdapter`](https://python-all.ru/3.0/library/logging.html#logging.LoggerAdapter) используются для удобной передачи контекстной информации в вызовы логирования. Пример использования см. в разделе [добавление контекстной информации в вывод логирования](https://python-all.ru/3.0/library/logging.html#context-info).18841885#### `[logging.LoggerAdapter]class logging.LoggerAdapter(logger, extra)`18861887Возвращает экземпляр [`LoggerAdapter`](https://python-all.ru/3.0/library/logging.html#logging.LoggerAdapter), инициализированный базовым экземпляром `Logger` и объектом, подобным словарю.18881889#### `[logging.LoggerAdapter.process]process(msg, kwargs)`18901891Изменяет сообщение и/или именованные аргументы, переданные в вызов логирования, чтобы вставить контекстную информацию. Эта реализация берёт объект, переданный как18921893*extra*18941895конструктору, и добавляет его в18961897*kwargs*18981899с ключом 'extra'. Возвращаемое значение – кортеж (19001901*msg*19021903,19041905*kwargs*19061907), содержащий (возможно изменённые) версии переданных аргументов.19081909Помимо перечисленного, [`LoggerAdapter`](https://python-all.ru/3.0/library/logging.html#logging.LoggerAdapter) поддерживает все методы логирования класса `Logger`, то есть [`debug()`](https://python-all.ru/3.0/library/logging.html#logging.debug), [`info()`](https://python-all.ru/3.0/library/logging.html#logging.info), [`warning()`](https://python-all.ru/3.0/library/logging.html#logging.warning), [`error()`](https://python-all.ru/3.0/library/logging.html#logging.error), [`exception()`](https://python-all.ru/3.0/library/logging.html#logging.exception), [`critical()`](https://python-all.ru/3.0/library/logging.html#logging.critical) и [`log()`](https://python-all.ru/3.0/library/logging.html#logging.log). Эти методы имеют те же сигнатуры, что и их аналоги в `Logger`, поэтому экземпляры обоих типов можно использовать взаимозаменямо.19101911## Потокобезопасность19121913Модуль logging спроектирован так, чтобы быть потокобезопасным без каких-либо специальных действий требуемых от его клиентов. Это достигается за счёт использования threading блокировок; одна блокировка сериализует доступ к общим данным модуля, а каждый обработчик также создаёт блокировку для сериализации доступа к своему нижележащему вводу-выводу.19141915## Конфигурация19161917### Функции настройки19181919Следующие функции настраивают модуль логирования. Они находятся в модуле `logging.config`. Их использование необязательно – настроить модуль логирования можно как с помощью этих функций, так и напрямую через основной API (определённый в самом `logging`) и определением обработчиков, объявленных либо в `logging`, либо в `logging.handlers`.19201921#### `[logging.fileConfig]logging.fileConfig(fname[, defaults])`19221923Читает конфигурацию логирования из файла формата19241925[`configparser`](https://python-all.ru/3.0/library/configparser.html#module-configparser)19261927с именем19281929*fname*19301931. Эту функцию можно вызывать несколько раз из приложения, что позволяет конечному пользователю выбирать из различных готовых конфигураций (если разработчик предоставляет механизм для отображения вариантов и загрузки выбранной конфигурации). Значения по умолчанию, передаваемые в ConfigParser, можно указать в аргументе19321933*defaults*19341935.19361937#### `[logging.listen]logging.listen([port])`19381939Запускает сокет-сервер на указанном порту и ожидает новые конфигурации. Если порт не указан, используется значение по умолчанию модуля `DEFAULT_LOGGING_CONFIG_PORT`. Конфигурации логирования отправляются в виде файла, пригодного для обработки функцией [`fileConfig()`](https://python-all.ru/3.0/library/logging.html#logging.fileConfig). Возвращает экземпляр `поток`, на котором можно вызвать `start()` для запуска сервера и к которому при необходимости можно применить `join()`. Чтобы остановить сервер, вызовите [`stopListening()`](https://python-all.ru/3.0/library/logging.html#logging.stopListening).19401941Чтобы отправить конфигурацию в сокет, прочитайте файл конфигурации и отправьте его в сокет в виде строки байтов, перед которой идёт четырёхбайтовая длина, упакованная в двоичном виде с помощью `struct.pack('>L', n)`.19421943#### `[logging.stopListening]logging.stopListening()`19441945Останавливает сервер прослушивания, созданный вызовом19461947[`listen()`](https://python-all.ru/3.0/library/logging.html#logging.listen)19481949. Обычно вызывается перед вызовом19501951`join()`19521953на возвращаемом значении19541955[`listen()`](https://python-all.ru/3.0/library/logging.html#logging.listen)19561957.19581959### Формат файла конфигурации19601961The configuration file format understood by [`fileConfig()`](https://python-all.ru/3.0/library/logging.html#logging.fileConfig) is based on [`configparser`](https://python-all.ru/3.0/library/configparser.html#module-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]`.19621963Примеры таких разделов в файле приведены ниже.19641965```python1966[loggers]1967keys=root,log02,log03,log04,log05,log06,log0719681969[handlers]1970keys=hand01,hand02,hand03,hand04,hand05,hand06,hand07,hand08,hand0919711972[formatters]1973keys=form01,form02,form03,form04,form05,form06,form07,form08,form091974```19751976Корневой регистратор должен указывать уровень и список обработчиков. Пример раздела корневого регистратора приведен ниже.19771978```python1979[logger_root]1980level=NOTSET1981handlers=hand011982```19831984The `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()`](https://python-all.ru/3.0/library/functions.html#eval)uated in the context of the `logging` package’s namespace.19851986The `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.19871988Для регистраторов, отличных от корневого, требуется некоторая дополнительная информация. Это проиллюстрировано следующим примером.19891990```python1991[logger_parser]1992level=DEBUG1993handlers=hand011994propagate=11995qualname=compiler.parser1996```19971998The `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.19992000Разделы, которые задают конфигурацию обработчика, иллюстрируются следующим примером.20012002```python2003[handler_hand01]2004class=StreamHandler2005level=NOTSET2006formatter=form012007args=(sys.stdout,)2008```20092010Запись `class` указывает класс обработчика (определяемый с помощью [`eval()`](https://python-all.ru/3.0/library/functions.html#eval) в пространстве имён пакета `logging`). Значение `level` интерпретируется так же, как для логгеров, а `NOTSET` означает «логировать всё».20112012The `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.20132014Запись `args` при вычислении [`eval()`](https://python-all.ru/3.0/library/functions.html#eval) в контексте пространства имён пакета `logging` представляет собой список аргументов для конструктора класса обработчика. Обратитесь к конструкторам соответствующих обработчиков или к примерам ниже, чтобы узнать, как формируются типичные записи.20152016```python2017[handler_hand02]2018class=FileHandler2019level=DEBUG2020formatter=form022021args=('python.log', 'w')20222023[handler_hand03]2024class=handlers.SocketHandler2025level=INFO2026formatter=form032027args=('localhost', handlers.DEFAULT_TCP_LOGGING_PORT)20282029[handler_hand04]2030class=handlers.DatagramHandler2031level=WARN2032formatter=form042033args=('localhost', handlers.DEFAULT_UDP_LOGGING_PORT)20342035[handler_hand05]2036class=handlers.SysLogHandler2037level=ERROR2038formatter=form052039args=(('localhost', handlers.SYSLOG_UDP_PORT), handlers.SysLogHandler.LOG_USER)20402041[handler_hand06]2042class=handlers.NTEventLogHandler2043level=CRITICAL2044formatter=form062045args=('Python Application', '', 'Application')20462047[handler_hand07]2048class=handlers.SMTPHandler2049level=WARN2050formatter=form072051args=('localhost', 'from@abc', ['user1@abc', 'user2@xyz'], 'Logger Subject')20522053[handler_hand08]2054class=handlers.MemoryHandler2055level=NOTSET2056formatter=form082057target=2058args=(10, ERROR)20592060[handler_hand09]2061class=handlers.HTTPHandler2062level=NOTSET2063formatter=form092064args=('localhost:9022', '/log', 'GET')2065```20662067Разделы, которые задают конфигурацию форматировщика, обычно выглядят следующим образом.20682069```python2070[formatter_form01]2071format=F1 %(asctime)s %(levelname)s %(message)s2072datefmt=2073class=logging.Formatter2074```20752076Запись `format` – это общая строка формата, а запись `datefmt` – это строка формата даты/времени, совместимая с `strftime()`. Если она пуста, пакет подставляет дату/время в формате ISO8601, что почти эквивалентно указанию строки формата даты `"%Y-%m-%d %H:%M:%S"`. Формат ISO8601 также задаёт миллисекунды, которые добавляются к результату использования указанной выше строки формата через запятую. Пример времени в формате ISO8601: `2003-01-23 00:29:50,411`.20772078Запись `class` необязательна. Она указывает имя класса форматировщика (в виде точечного имени модуля и класса). Эта опция полезна для создания экземпляра подкласса [`Formatter`](https://python-all.ru/3.0/library/logging.html#logging.Formatter). Подклассы [`Formatter`](https://python-all.ru/3.0/library/logging.html#logging.Formatter) могут отображать трассировки исключений в расширенном или сжатом формате.20792080### Пример сервера конфигурации20812082Вот пример модуля, использующего сервер конфигурации журналирования:20832084```python2085import logging2086import logging.config2087import time2088import os20892090# Прочитать начальный конфигурационный файл.2091logging.config.fileConfig("logging.conf")20922093# Создать и запустить слушатель на порту 9999.2094t = logging.config.listen(9999)2095t.start()20962097logger = logging.getLogger("simpleExample")20982099try:2100    # Прокрутить вызовы логирования, чтобы увидеть разницу.2101    # Создавать новые конфигурации до нажатия Ctrl+C.2102    while True:2103        logger.debug("debug message")2104        logger.info("info message")2105        logger.warn("warn message")2106        logger.error("error message")2107        logger.critical("critical message")2108        time.sleep(5)2109except KeyboardInterrupt:2110    # очистка2111    logging.config.stopListening()2112    t.join()2113```21142115А вот скрипт, который принимает имя файла и отправляет этот файл на сервер, предварив его двоично-закодированной длиной, в качестве новой конфигурации журналирования:21162117```python2118#!/usr/bin/env python2119import socket, sys, struct21202121data_to_send = open(sys.argv[1], "r").read()21222123HOST = 'localhost'2124PORT = 99992125s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)2126print("connecting...")2127s.connect((HOST, PORT))2128print("sending config...")2129s.send(struct.pack(">L", len(data_to_send)))2130s.send(data_to_send)2131s.close()2132print("complete")2133```21342135## Дополнительные примеры21362137### Несколько обработчиков и форматировщиков21382139Логгеры – это обычные объекты Python. У метода `addHandler()` нет минимального или максимального ограничения на количество добавляемых обработчиков. Иногда бывает полезно, чтобы приложение записывало все сообщения всех уровней в текстовый файл и одновременно выводило ошибки и выше на консоль. Чтобы это настроить, достаточно сконфигурировать соответствующие обработчики. Вызовы логирования в коде приложения останутся без изменений. Вот небольшая модификация предыдущего простого примера конфигурации на основе модуля:21402141```python2142import logging21432144logger = logging.getLogger("simple_example")2145logger.setLevel(logging.DEBUG)2146# создать файловый обработчик, записывающий даже отладочные сообщения2147fh = logging.FileHandler("spam.log")2148fh.setLevel(logging.DEBUG)2149# создать консольный обработчик с более высоким уровнем логирования2150ch = logging.StreamHandler()2151ch.setLevel(logging.ERROR)2152# создать форматтер и добавить его к обработчикам2153formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")2154ch.setFormatter(formatter)2155fh.setFormatter(formatter)2156# добавить обработчики в логгер2157logger.addHandler(ch)2158logger.addHandler(fh)21592160# код "application"2161logger.debug("debug message")2162logger.info("info message")2163logger.warn("warn message")2164logger.error("error message")2165logger.critical("critical message")2166```21672168Обратите внимание, что код «приложения» безразличен к наличию нескольких обработчиков. Всё, что изменилось – это добавление и настройка нового обработчика с именем *fh*.21692170Возможность создавать новые обработчики с фильтрами более высокого или низкого уровня серьезности может быть очень полезна при написании и тестировании приложения. Вместо использования множества операторов `print` для отладки используйте `logger.debug`: в отличие от операторов print, которые вам придется удалить или закомментировать позже, операторы logger.debug могут оставаться в исходном коде и оставаться неактивными до тех пор, пока они снова не понадобятся. В этом случае единственное, что нужно сделать, – изменить уровень серьезности логгера и/или обработчика на debug.21712172### Использование логирования в нескольких модулях21732174Выше упоминалось, что многократные вызовы `logging.getLogger('someLogger')` возвращают ссылку на один и тот же объект логгера. Это верно не только в пределах одного модуля, но и между модулями, пока они находятся в одном процессе интерпретатора Python. Это справедливо для ссылок на один и тот же объект; кроме того, код приложения может определить и настроить родительский логгер в одном модуле и создать (но не настраивать) дочерний логгер в другом модуле, и все вызовы логгера к дочернему будут передаваться родительскому. Вот пример главного модуля:21752176```python2177import logging2178import auxiliary_module21792180# создать логгер с именем "spam_application"2181logger = logging.getLogger("spam_application")2182logger.setLevel(logging.DEBUG)2183# создать файловый обработчик, записывающий даже отладочные сообщения2184fh = logging.FileHandler("spam.log")2185fh.setLevel(logging.DEBUG)2186# создать консольный обработчик с более высоким уровнем логирования2187ch = logging.StreamHandler()2188ch.setLevel(logging.ERROR)2189# создать форматтер и добавить его к обработчикам2190formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")2191fh.setFormatter(formatter)2192ch.setFormatter(formatter)2193# добавить обработчики к логгеру2194logger.addHandler(fh)2195logger.addHandler(ch)21962197logger.info("creating an instance of auxiliary_module.Auxiliary")2198a = auxiliary_module.Auxiliary()2199logger.info("created an instance of auxiliary_module.Auxiliary")2200logger.info("calling auxiliary_module.Auxiliary.do_something")2201a.do_something()2202logger.info("finished auxiliary_module.Auxiliary.do_something")2203logger.info("calling auxiliary_module.some_function()")2204auxiliary_module.some_function()2205logger.info("done with auxiliary_module.some_function()")2206```22072208А вот вспомогательный модуль:22092210```python2211import logging22122213# create logger2214module_logger = logging.getLogger("spam_application.auxiliary")22152216class Auxiliary:2217    def __init__(self):2218        self.logger = logging.getLogger("spam_application.auxiliary.Auxiliary")2219        self.logger.info("creating an instance of Auxiliary")2220    def do_something(self):2221        self.logger.info("doing something")2222        a = 1 + 12223        self.logger.info("done doing something")22242225def some_function():2226    module_logger.info("received a call to \"some_function\"")2227```22282229Вывод выглядит так:22302231```python22322005-03-23 23:47:11,663 - spam_application - INFO -2233   creating an instance of auxiliary_module.Auxiliary22342005-03-23 23:47:11,665 - spam_application.auxiliary.Auxiliary - INFO -2235   creating an instance of Auxiliary22362005-03-23 23:47:11,665 - spam_application - INFO -2237   created an instance of auxiliary_module.Auxiliary22382005-03-23 23:47:11,668 - spam_application - INFO -2239   calling auxiliary_module.Auxiliary.do_something22402005-03-23 23:47:11,668 - spam_application.auxiliary.Auxiliary - INFO -2241   doing something22422005-03-23 23:47:11,669 - spam_application.auxiliary.Auxiliary - INFO -2243   done doing something22442005-03-23 23:47:11,670 - spam_application - INFO -2245   finished auxiliary_module.Auxiliary.do_something22462005-03-23 23:47:11,671 - spam_application - INFO -2247   calling auxiliary_module.some_function()22482005-03-23 23:47:11,672 - spam_application.auxiliary - INFO -2249   received a call to "some_function"22502005-03-23 23:47:11,673 - spam_application - INFO -2251   done with auxiliary_module.some_function()2252```2253