logging.md
1> **Источник:** https://python-all.ru/3.1/library/logging.html2>3> «Документация Python на русском» – неофициальный перевод официальной документации Python: версии от 2.6 до 3.16, полнотекстовый поиск, английский оригинал рядом с переводом. Эта Markdown-версия страницы предназначена для работы с LLM: вставьте её в ChatGPT, Claude или Cursor.45---67# 15.6. `logging` – средство логирования для Python89Этот модуль определяет функции и классы, реализующие гибкую систему логирования ошибок для приложений.1011Логирование выполняется вызовом методов экземпляров класса `Logger` (далее называемых *логгерами*). Каждый экземпляр имеет имя, и они концептуально организованы в иерархию пространств имён с использованием точек (.) в качестве разделителей. Например, логгер с именем «scan» является родительским для логгеров «scan.text», «scan.html» и «scan.pdf». Имена логгеров могут быть любыми и указывают на область приложения, из которой исходит регистрируемое сообщение.1213Регистрируемые сообщения также имеют связанные с ними уровни важности. Предоставляемые по умолчанию уровни: `DEBUG`, `INFO`, `WARNING`, `ERROR` и `CRITICAL`. Для удобства уровень важности сообщения указывается вызовом соответствующего метода `Logger`. Это методы [`debug()`](https://python-all.ru/3.1/library/logging.html#logging.debug), [`info()`](https://python-all.ru/3.1/library/logging.html#logging.info), [`warning()`](https://python-all.ru/3.1/library/logging.html#logging.warning), [`error()`](https://python-all.ru/3.1/library/logging.html#logging.error) и [`critical()`](https://python-all.ru/3.1/library/logging.html#logging.critical), которые соответствуют уровням по умолчанию. Вы не обязаны использовать только эти уровни: можно задать собственные и применять более общий метод `Logger` – [`log()`](https://python-all.ru/3.1/library/logging.html#logging.log), который принимает явный аргумент уровня.1415## 15.6.1. Учебное пособие по логированию1617Ключевое преимущество наличия API логирования, предоставляемого модулем стандартной библиотеки, заключается в том, что все модули Python могут участвовать в логировании, поэтому журнал приложения может включать сообщения от сторонних модулей.1819Конечно, можно регистрировать сообщения с разными уровнями подробности или направлять их в разные места. Поддержка записи сообщений в файлы, по HTTP GET/POST, электронной почте через SMTP, через обычные сокеты или через механизмы логирования, специфичные для ОС, – всё это поддерживается стандартным модулем. Вы также можете создать собственный класс назначения журнала, если ваши особые требования не удовлетворяются ни одним из встроенных классов.2021### 15.6.1.1. Простые примеры2223Большинство приложений, вероятно, захотят вести логирование в файл, поэтому начнём с этого случая. С помощью функции [`basicConfig()`](https://python-all.ru/3.1/library/logging.html#logging.basicConfig) можно настроить обработчик по умолчанию так, чтобы отладочные сообщения записывались в файл (в примере предполагается, что у вас есть соответствующие разрешения для создания файла с именем *example.log* в текущем каталоге):2425```python26import logging27LOG_FILENAME = 'example.log'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.1/library/logging.html#logging.basicConfig) со значением `'w'`. Однако вместо того чтобы управлять размером файла вручную, проще использовать `RotatingFileHandler`:4041```python42import glob43import logging44import logging.handlers4546LOG_FILENAME = '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```python72logging_rotatingfile_example.out73logging_rotatingfile_example.out.174logging_rotatingfile_example.out.275logging_rotatingfile_example.out.376logging_rotatingfile_example.out.477logging_rotatingfile_example.out.578```7980Самый текущий файл всегда называется `logging_rotatingfile_example.out`, и каждый раз, когда он достигает предельного размера, он переименовывается с суффиксом `.1`. Каждый из существующих резервных файлов переименовывается для увеличения суффикса (`.1` становится `.2` и т.д.), а файл `.6` удаляется.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### 15.6.1.2. Логгеры152153Библиотека logging использует модульный подход и предлагает несколько категорий компонентов: логгеры, обработчики, фильтры и форматировщики. Логгеры предоставляют интерфейс, который напрямую используется кодом приложения. Обработчики отправляют записи журнала в соответствующее место назначения. Фильтры предоставляют более тонкое средство для определения того, какие записи журнала следует передать обработчику. Форматировщики задают структуру итоговой записи журнала.154155Объекты `Logger` выполняют тройную задачу. Во-первых, они предоставляют коду приложения несколько методов, чтобы приложения могли регистрировать сообщения во время выполнения. Во-вторых, объекты логгера определяют, на какие сообщения следует реагировать, на основе степени важности (стандартное средство фильтрации) или объектов-фильтров. В-третьих, объекты логгера передают соответствующие сообщения всем заинтересованным обработчикам.156157Наиболее часто используемые методы объектов логгера делятся на две категории: настройка и отправка сообщений.158159- [`Logger.setLevel()`](https://python-all.ru/3.1/library/logging.html#logging.Logger.setLevel) задаёт наименьшую степень важности сообщения, которое будет обрабатывать логгер; при этом debug – это наименьший встроенный уровень важности, а critical – наибольший. Например, если уровень важности установлен в info, логгер будет обрабатывать только сообщения info, warning, error и critical, игнорируя сообщения debug.160- [`Logger.addFilter()`](https://python-all.ru/3.1/library/logging.html#logging.Logger.addFilter) и [`Logger.removeFilter()`](https://python-all.ru/3.1/library/logging.html#logging.Logger.removeFilter) добавляют и удаляют объекты фильтров из объекта логгера. В данном учебном пособии фильтры не рассматриваются.161162После настройки объекта регистратора следующие методы создают сообщения журнала:163164- [`Logger.debug()`](https://python-all.ru/3.1/library/logging.html#logging.Logger.debug), [`Logger.info()`](https://python-all.ru/3.1/library/logging.html#logging.Logger.info), [`Logger.warning()`](https://python-all.ru/3.1/library/logging.html#logging.Logger.warning), [`Logger.error()`](https://python-all.ru/3.1/library/logging.html#logging.Logger.error) и [`Logger.critical()`](https://python-all.ru/3.1/library/logging.html#logging.Logger.critical) создают записи лога с сообщением и уровнем, соответствующими их именам методов. Сообщение – это строка формата, которая может содержать стандартный синтаксис подстановки строк `%s`, `%d`, `%f` и так далее. Остальные аргументы – список объектов, соответствующих полям подстановки в сообщении. Что касается `**kwargs`, методы логирования учитывают только ключевое слово `exc_info` и используют его для определения, нужно ли записывать информацию об исключении.165- [`Logger.exception()`](https://python-all.ru/3.1/library/logging.html#logging.Logger.exception) создает сообщение лога, аналогичное [`Logger.error()`](https://python-all.ru/3.1/library/logging.html#logging.Logger.error). Отличие в том, что [`Logger.exception()`](https://python-all.ru/3.1/library/logging.html#logging.Logger.exception) выводит вместе с ним трассировку стека. Вызывайте этот метод только из обработчика исключений.166- [`Logger.log()`](https://python-all.ru/3.1/library/logging.html#logging.Logger.log) принимает уровень лога в качестве явного аргумента. Это немного более многословно для записи сообщений, чем использование удобных методов уровня лога, перечисленных выше, но именно так производится логирование на пользовательских уровнях лога.167168[`getLogger()`](https://python-all.ru/3.1/library/logging.html#logging.getLogger) возвращает ссылку на экземпляр логгера с указанным именем, если оно задано, или `root` в противном случае. Имена представляют собой иерархические структуры, разделённые точками. При многократных вызовах [`getLogger()`](https://python-all.ru/3.1/library/logging.html#logging.getLogger) с одним и тем же именем возвращается ссылка на один и тот же объект логгера. Логгеры, находящиеся ниже в иерархическом списке, являются дочерними по отношению к логгерам, находящимся выше. Например, для логгера с именем `foo` логгеры с именами `foo.bar`, `foo.bar.baz` и `foo.bam` являются потомками `foo`. Дочерние логгеры передают сообщения вверх обработчикам, связанным с их логгерами-предками. Благодаря этому нет необходимости определять и настраивать обработчики для всех логгеров, используемых приложением. Достаточно настроить обработчики для логгера верхнего уровня и создавать дочерние логгеры по мере необходимости.169170### 15.6.1.3. Обработчики171172Объекты `Handler` отвечают за отправку соответствующих сообщений журнала (на основе их важности) в указанное место назначения. Объекты логгера могут добавлять к себе ноль или более объектов обработчиков с помощью метода `addHandler()`. В качестве примера: приложение может захотеть отправлять все сообщения в файл журнала, все сообщения уровня error и выше – в stdout, а все сообщения уровня critical – на адрес электронной почты. Для этого требуется три отдельных обработчика, каждый из которых отвечает за отправку сообщений определённого уровня важности в определённое место.173174Стандартная библиотека включает довольно много типов обработчиков; в данном учебном пособии используются только [`StreamHandler`](https://python-all.ru/3.1/library/logging.html#logging.StreamHandler) и [`FileHandler`](https://python-all.ru/3.1/library/logging.html#logging.FileHandler).175176В обработчике очень мало методов, которые должны беспокоить разработчиков приложений. Единственные методы обработчика, актуальные для разработчиков, использующих встроенные объекты-обработчики (то есть не создающих собственные обработчики), – это следующие методы настройки:177178- Метод [`Handler.setLevel()`](https://python-all.ru/3.1/library/logging.html#logging.Handler.setLevel), как и у объектов логгера, задаёт минимальный уровень серьезности, который будет отправлен в соответствующее место назначения. Зачем нужны два метода `setLevel()`? Уровень, установленный в логгере, определяет, сообщения какой серьезности он будет передавать своим обработчикам. Уровень, установленный в каждом обработчике, определяет, какие сообщения этот обработчик будет отправлять дальше.179- `setFormatter()` выбирает объект Formatter для использования этим обработчиком.180- `addFilter()` и `removeFilter()` соответственно добавляют и удаляют объекты фильтров у обработчиков.181182Прикладной код не должен напрямую создавать и использовать экземпляры `Handler`. Вместо этого класс `Handler` является базовым классом, который определяет интерфейс, который должны иметь все обработчики, и устанавливает некоторое поведение по умолчанию, которое могут использовать (или переопределять) дочерние классы.183184### 15.6.1.4. Форматировщики185186Объекты Formatter настраивают окончательный порядок, структуру и содержимое сообщения журнала. В отличие от базового класса `logging.Handler`, код приложения может создавать экземпляры классов форматировщиков, хотя при необходимости особого поведения можно создать подкласс форматировщика. Конструктор принимает два необязательных аргумента: строку формата сообщения и строку формата даты. Если строка формата сообщения не задана, по умолчанию используется исходное сообщение. Если строка формата даты не задана, используется следующий формат даты по умолчанию:187188```python189%Y-%m-%d %H:%M:%S190```191192с добавленными в конце миллисекундами.193194Строка формата сообщения использует подстановку строк в стиле `%(<dictionary key>)s`; возможные ключи описаны в разделе [*Объекты Formatter*](https://python-all.ru/3.1/library/logging.html#formatter-objects).195196Следующая строка формата сообщения будет записывать время в удобочитаемом формате, уровень серьёзности сообщения и содержимое сообщения в указанном порядке:197198```python199"%(asctime)s - %(levelname)s - %(message)s"200```201202Форматтеры используют настраиваемую пользователем функцию для преобразования времени создания записи в кортеж. По умолчанию используется [`time.localtime()`](https://python-all.ru/3.1/library/time.html#time.localtime); чтобы изменить это для конкретного экземпляра форматтера, установите атрибут `converter` экземпляра на функцию с той же сигнатурой, что и [`time.localtime()`](https://python-all.ru/3.1/library/time.html#time.localtime) или [`time.gmtime()`](https://python-all.ru/3.1/library/time.html#time.gmtime). Чтобы изменить это для всех форматтеров, например, если вы хотите, чтобы все время логирования отображалось в GMT, установите атрибут `converter` в классе Formatter (на `time.gmtime` для отображения в GMT).203204### 15.6.1.5. Настройка логирования205206Программисты могут настраивать логирование двумя способами: явно создавать логгеры, обработчики и форматтеры в главном модуле с помощью перечисленных выше методов настройки (на Python) или создавая конфигурационный файл логирования. Следующий код – пример настройки очень простого логгера, обработчика консоли и простого форматтера в модуле Python:207208```python209import logging210211# создать логгер212logger = logging.getLogger("simple_example")213logger.setLevel(logging.DEBUG)214# создать обработчик консоли и установить уровень DEBUG215ch = logging.StreamHandler()216ch.setLevel(logging.DEBUG)217# создать форматтер218formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")219# добавить форматтер в ch220ch.setFormatter(formatter)221# добавить ch в логгер222logger.addHandler(ch)223224# код "application"225logger.debug("debug message")226logger.info("info message")227logger.warn("warn message")228logger.error("error message")229logger.critical("critical message")230```231232Запуск этого модуля из командной строки выдает следующий результат:233234```python235$ python simple_logging_module.py2362005-03-19 15:10:26,618 - simple_example - DEBUG - debug message2372005-03-19 15:10:26,620 - simple_example - INFO - info message2382005-03-19 15:10:26,695 - simple_example - WARNING - warn message2392005-03-19 15:10:26,697 - simple_example - ERROR - error message2402005-03-19 15:10:26,773 - simple_example - CRITICAL - critical message241```242243Следующий модуль Python создает логгер, обработчик и форматтер, почти идентичные приведенным в примере выше, с единственным отличием – имена объектов.244245```python246import logging247import logging.config248249logging.config.fileConfig("logging.conf")250251# создать логгер252logger = logging.getLogger("simpleExample")253254# код "application"255logger.debug("debug message")256logger.info("info message")257logger.warn("warn message")258logger.error("error message")259logger.critical("critical message")260```261262Вот файл logging.conf:263264```python265[loggers]266keys=root,simpleExample267268[handlers]269keys=consoleHandler270271[formatters]272keys=simpleFormatter273274[logger_root]275level=DEBUG276handlers=consoleHandler277278[logger_simpleExample]279level=DEBUG280handlers=consoleHandler281qualname=simpleExample282propagate=0283284[handler_consoleHandler]285class=StreamHandler286level=DEBUG287formatter=simpleFormatter288args=(sys.stdout,)289290[formatter_simpleFormatter]291format=%(asctime)s - %(name)s - %(levelname)s - %(message)s292datefmt=293```294295Результат почти идентичен примеру без файла конфигурации:296297```python298$ python simple_logging_config.py2992005-03-19 15:38:55,977 - simpleExample - DEBUG - debug message3002005-03-19 15:38:55,979 - simpleExample - INFO - info message3012005-03-19 15:38:56,054 - simpleExample - WARNING - warn message3022005-03-19 15:38:56,055 - simpleExample - ERROR - error message3032005-03-19 15:38:56,130 - simpleExample - CRITICAL - critical message304```305306Видно, что подход с файлом конфигурации имеет несколько преимуществ перед подходом с кодом Python: в первую очередь разделение конфигурации и кода, а также возможность для не-программистов легко изменять параметры журналирования.307308### 15.6.1.6. Настройка логирования для библиотеки309310При разработке библиотеки, которая использует логирование, необходимо уделить внимание её настройке. Если использующее приложение не использует логирование, а код библиотеки вызывает функции логирования, то в консоль выводится одноразовое сообщение “No handlers could be found for logger X.Y.Z”. Это сообщение предназначено для выявления ошибок в настройке логирования, но может сбить с толку разработчика приложения, который не знает о логировании в библиотеке.311312Помимо документирования того, как библиотека использует логирование, хороший способ настроить логирование библиотеки так, чтобы не появлялось ложное сообщение – добавить обработчик, который ничего не делает. Это предотвращает вывод сообщения, поскольку обработчик будет найден, но не производит никакого вывода. Если пользователь библиотеки настраивает логирование для приложения, то, предположительно, эта конфигурация добавит некоторые обработчики, и если уровни настроены правильно, то вызовы логирования в коде библиотеки будут отправлять вывод этим обработчикам, как обычно.313314Обработчик, который ничего не делает, можно просто определить следующим образом:315316```python317import logging318319class NullHandler(logging.Handler):320 def emit(self, record):321 pass322```323324Экземпляр этого обработчика следует добавить к корневому логгеру пространства имён логирования, используемого библиотекой. Если всё логирование библиотеки *foo* осуществляется с помощью логгеров с именами вида “foo.x.y”, то код:325326```python327import logging328329h = NullHandler()330logging.getLogger("foo").addHandler(h)331```332333должен дать желаемый эффект. Если организация выпускает несколько библиотек, то имя логгера можно указать как “orgname.foo” вместо просто “foo”.334335Новое в версии 3.1: Класс [`NullHandler`](https://python-all.ru/3.1/library/logging.html#logging.NullHandler).336337## 15.6.2. Уровни логирования338339Числовые значения уровней журналирования приведены в следующей таблице. Они представляют интерес в первую очередь, если вы хотите определить собственные уровни и хотите, чтобы они имели определенные значения относительно предопределенных уровней. Если вы определяете уровень с тем же числовым значением, он перезаписывает предопределенное значение; предопределенное имя теряется.340341| Уровень | Числовое значение |342| --- | --- |343| `CRITICAL` | 50 |344| `ERROR` | 40 |345| `WARNING` | 30 |346| `INFO` | 20 |347| `DEBUG` | 10 |348| `NOTSET` | 0 |349350Уровни также могут быть связаны с логгерами: они устанавливаются разработчиком или загружаются из сохраненной конфигурации журналирования. Когда на логгере вызывается метод журналирования, логгер сравнивает свой собственный уровень с уровнем, связанным с вызовом метода. Если уровень логгера выше уровня вызова метода, сообщение журналирования фактически не создается. Это базовый механизм, управляющий подробностью вывода журналирования.351352Сообщения логирования представляют собой экземпляры класса [`LogRecord`](https://python-all.ru/3.1/library/logging.html#logging.LogRecord). Когда логгер решает фактически зарегистрировать событие, из сообщения логирования создаётся экземпляр [`LogRecord`](https://python-all.ru/3.1/library/logging.html#logging.LogRecord).353354Сообщения логирования проходят через механизм диспетчеризации с помощью *обработчиков*, которые являются экземплярами подклассов класса `Handler`. Обработчики отвечают за то, чтобы записанное сообщение (в виде [`LogRecord`](https://python-all.ru/3.1/library/logging.html#logging.LogRecord)) оказалось в определённом месте (или наборе мест), полезном для целевой аудитории этого сообщения (конечные пользователи, персонал техподдержки, системные администраторы, разработчики). Обработчики получают экземпляры [`LogRecord`](https://python-all.ru/3.1/library/logging.html#logging.LogRecord), предназначенные для конкретных мест назначения. Каждый логгер может иметь ноль, один или несколько связанных с ним обработчиков (через метод `addHandler()` класса `Logger`). В дополнение к обработчикам, напрямую связанным с логгером, *все обработчики, связанные со всеми предками логгера*, вызываются для диспетчеризации сообщения (если только флаг *propagate* для логгера не установлен в ложное значение, после чего передача обработчикам-предкам прекращается).355356Как и для логгеров, для обработчиков могут быть заданы уровни. Уровень обработчика действует как фильтр так же, как и уровень логгера. Если обработчик решает фактически диспетчеризировать событие, используется метод `emit()` для отправки сообщения в место назначения. Большинству определённых пользователем подклассов `Handler` потребуется переопределить этот `emit()`.357358## 15.6.3. Полезные обработчики359360Помимо базового класса `Handler`, предоставляется множество полезных подклассов:3613621. Экземпляры [`StreamHandler`](https://python-all.ru/3.1/library/logging.html#logging.StreamHandler) отправляют сообщения в потоки (объекты, подобные файлам).3632. Экземпляры [`FileHandler`](https://python-all.ru/3.1/library/logging.html#logging.FileHandler) отправляют сообщения в файлы на диске.3643651. `BaseRotatingHandler` – это базовый класс для обработчиков, которые выполняют ротацию файлов журнала в определённый момент. Он не предназначен для прямого создания экземпляров. Вместо этого используйте [`RotatingFileHandler`](https://python-all.ru/3.1/library/logging.html#logging.handlers.RotatingFileHandler) или [`TimedRotatingFileHandler`](https://python-all.ru/3.1/library/logging.html#logging.handlers.TimedRotatingFileHandler).3662. Экземпляры [`RotatingFileHandler`](https://python-all.ru/3.1/library/logging.html#logging.handlers.RotatingFileHandler) отправляют сообщения в файлы на диске с поддержкой максимального размера файла журнала и ротации.3673. Экземпляры [`TimedRotatingFileHandler`](https://python-all.ru/3.1/library/logging.html#logging.handlers.TimedRotatingFileHandler) отправляют сообщения в файлы на диске, выполняя ротацию через заданные временные интервалы.3684. [`SocketHandler`](https://python-all.ru/3.1/library/logging.html#logging.handlers.SocketHandler) отправляют сообщения в TCP/IP сокеты.3695. [`DatagramHandler`](https://python-all.ru/3.1/library/logging.html#logging.handlers.DatagramHandler) отправляют сообщения в UDP сокеты.3706. Экземпляры [`SMTPHandler`](https://python-all.ru/3.1/library/logging.html#logging.handlers.SMTPHandler) отправляют сообщения на указанный адрес электронной почты.3717. Экземпляры [`SysLogHandler`](https://python-all.ru/3.1/library/logging.html#logging.handlers.SysLogHandler) отправляют сообщения демону syslog Unix, возможно, на удалённой машине.3728. Экземпляры [`NTEventLogHandler`](https://python-all.ru/3.1/library/logging.html#logging.handlers.NTEventLogHandler) отправляют сообщения в журнал событий Windows NT/2000/XP.3739. Экземпляры [`MemoryHandler`](https://python-all.ru/3.1/library/logging.html#logging.handlers.MemoryHandler) отправляют сообщения в буфер в памяти, который сбрасывается при выполнении определённых условий.37410. Экземпляры [`HTTPHandler`](https://python-all.ru/3.1/library/logging.html#logging.handlers.HTTPHandler) отправляют сообщения на HTTP-сервер, используя семантику `GET` или `POST`.37511. Экземпляры [`WatchedFileHandler`](https://python-all.ru/3.1/library/logging.html#logging.handlers.WatchedFileHandler) следят за файлом, в который ведут журнал. Если файл изменяется, он закрывается и открывается заново по тому же имени. Этот обработчик полезен только в Unix-подобных системах; Windows не поддерживает используемый механизм.3763771. Экземпляры [`NullHandler`](https://python-all.ru/3.1/library/logging.html#logging.NullHandler) ничего не делают с сообщениями об ошибках. Они используются разработчиками библиотек, которые хотят использовать логирование, но избежать сообщения “No handlers could be found for logger XXX”, которое может отображаться, если пользователь библиотеки не настроил логирование. См. [*Настройка логирования для библиотеки*](https://python-all.ru/3.1/library/logging.html#library-config) для получения дополнительной информации.378379Новое в версии 3.1: Класс [`NullHandler`](https://python-all.ru/3.1/library/logging.html#logging.NullHandler).380381Классы [`NullHandler`](https://python-all.ru/3.1/library/logging.html#logging.NullHandler), [`StreamHandler`](https://python-all.ru/3.1/library/logging.html#logging.StreamHandler) и [`FileHandler`](https://python-all.ru/3.1/library/logging.html#logging.FileHandler) определены в основном пакете logging. Остальные обработчики определены в подмодуле `logging.handlers`. (Существует также ещё один подмодуль, `logging.config`, для функциональности настройки.)382383Зарегистрированные сообщения форматируются для отображения через экземпляры класса [`Formatter`](https://python-all.ru/3.1/library/logging.html#logging.Formatter). Они инициализируются форматной строкой, подходящей для использования с оператором % и словарём.384385Для форматирования нескольких сообщений в одном пакете можно использовать экземпляры `BufferingFormatter`. Помимо строки формата (которая применяется к каждому сообщению в пакете), предусмотрены строки формата для заголовка и завершающей части.386387Когда фильтрации на основе уровня логгера и/или уровня обработчика недостаточно, экземпляры [`Filter`](https://python-all.ru/3.1/library/logging.html#logging.Filter) можно добавить как к экземплярам `Logger`, так и к `Handler` (через их метод `addFilter()`). Прежде чем решить обрабатывать сообщение дальше, и логгеры, и обработчики запрашивают разрешение у всех своих фильтров. Если любой фильтр возвращает ложное значение, сообщение дальше не обрабатывается.388389Базовая функциональность [`Filter`](https://python-all.ru/3.1/library/logging.html#logging.Filter) позволяет фильтровать по конкретному имени средства журналирования. Если эта возможность используется, через фильтр пропускаются сообщения, отправленные указанному средству журналирования и его дочерним элементам, а все остальные отбрасываются.390391## 15.6.4. Функции уровня модуля392393В дополнение к классам, описанным выше, существует ряд функций уровня модуля.394395#### `logging.getLogger(name=None)`396397Возвращает логгер с указанным именем или, если name равно `None`, возвращает логгер, являющийся корневым логгером иерархии. Если имя указано, оно обычно представляет собой иерархическое имя, разделённое точками, например *“a”*, *“a.b”* или *“a.b.c.d”*. Выбор этих имён полностью зависит от разработчика, использующего логирование.398399Все вызовы этой функции с одним и тем же именем возвращают один и тот же экземпляр логгера. Это означает, что экземпляры логгера никогда не нужно передавать между различными частями приложения.400401#### `logging.getLoggerClass()`402403Возвращает либо стандартный класс `Logger`, либо последний класс, переданный в [`setLoggerClass()`](https://python-all.ru/3.1/library/logging.html#logging.setLoggerClass). Эта функция может быть вызвана из определения нового класса, чтобы гарантировать, что установка пользовательского класса `Logger` не отменит настройки, уже применённые другим кодом. Например:404405```python406class MyLogger(logging.getLoggerClass()):407 # ... переопределить поведение здесь408```409410#### `logging.debug(msg, *args, **kwargs)`411412Записывает сообщение с уровнем `DEBUG` в корневой регистратор. *msg* – это форматная строка сообщения, а *args* – аргументы, которые объединяются с *msg* с помощью оператора форматирования строк. (Обратите внимание, что это означает, что в форматной строке можно использовать ключевые слова вместе с единственным аргументом-словарём.)413414В *kwargs* проверяются два именованных аргумента: *exc\_info*, который, если его значение не равно false, приводит к добавлению информации об исключении в сообщение логирования. Если предоставлен кортеж исключения (в формате, возвращаемом [`sys.exc_info()`](https://python-all.ru/3.1/library/sys.html#sys.exc_info)), он используется; в противном случае вызывается [`sys.exc_info()`](https://python-all.ru/3.1/library/sys.html#sys.exc_info) для получения информации об исключении.415416Другой необязательный именованный аргумент – *extra*, который можно использовать для передачи словаря, заполняющего \_\_dict\_\_ объекта LogRecord, созданного для события логирования, пользовательскими атрибутами. Эти пользовательские атрибуты затем можно использовать по своему усмотрению. Например, их можно включить в записываемые сообщения. Пример:417418```python419FORMAT = "%(asctime)-15s %(clientip)s %(user)-8s %(message)s"420logging.basicConfig(format=FORMAT)421d = {'clientip': '192.168.0.1', 'user': 'fbloggs'}422logging.warning("Protocol problem: %s", "connection reset", extra=d)423```424425выведет что-то вроде:426427```python4282006-02-08 22:20:02,165 192.168.0.1 fbloggs Protocol problem: connection reset429```430431Ключи в словаре, переданном в *extra*, не должны пересекаться с ключами, используемыми системой логирования. (См. документацию [`Formatter`](https://python-all.ru/3.1/library/logging.html#logging.Formatter) для получения дополнительной информации о том, какие ключи используются системой логирования.)432433Если вы решите использовать эти атрибуты в логируемых сообщениях, нужно соблюдать осторожность. Например, в приведённом выше примере [`Formatter`](https://python-all.ru/3.1/library/logging.html#logging.Formatter) настроен с форматной строкой, которая ожидает 'clientip' и 'user' в словаре атрибутов LogRecord. Если они отсутствуют, сообщение не будет записано, так как возникнет исключение форматирования строки. Поэтому в этом случае всегда нужно передавать словарь *extra* с этими ключами.434435Хотя это может быть раздражающим, эта функция предназначена для использования в специализированных обстоятельствах, таких как многопоточные серверы, где один и тот же код выполняется во многих контекстах, и интересующие условия зависят от этого контекста (например, IP-адрес удалённого клиента и имя аутентифицированного пользователя в приведённом выше примере). В таких обстоятельствах, скорее всего, будут использоваться специализированные [`Formatter`](https://python-all.ru/3.1/library/logging.html#logging.Formatter) с конкретными `Handler`.436437#### `logging.info(msg, *args, **kwargs)`438439Записывает сообщение с уровнем440441`INFO`442443в корневой регистратор. Аргументы интерпретируются так же, как для444445[`debug()`](https://python-all.ru/3.1/library/logging.html#logging.debug)446447.448449#### `logging.warning(msg, *args, **kwargs)`450451Регистрирует сообщение с уровнем452453`WARNING`454455в корневом логгере. Аргументы интерпретируются так же, как для456457[`debug()`](https://python-all.ru/3.1/library/logging.html#logging.debug)458459.460461#### `logging.error(msg, *args, **kwargs)`462463Записывает сообщение с уровнем464465`ERROR`466467в корневой регистратор. Аргументы интерпретируются так же, как для468469[`debug()`](https://python-all.ru/3.1/library/logging.html#logging.debug)470471.472473#### `logging.critical(msg, *args, **kwargs)`474475Регистрирует сообщение с уровнем476477`CRITICAL`478479в корневом логгере. Аргументы интерпретируются так же, как для480481[`debug()`](https://python-all.ru/3.1/library/logging.html#logging.debug)482483.484485#### `logging.exception(msg, *args)`486487Регистрирует сообщение с уровнем488489`ERROR`490491в корневом логгере. Аргументы интерпретируются так же, как для492493[`debug()`](https://python-all.ru/3.1/library/logging.html#logging.debug)494495. Информация об исключении добавляется в сообщение журнала. Эта функция должна вызываться только из обработчика исключений.496497#### `logging.log(level, msg, *args, **kwargs)`498499Регистрирует сообщение с уровнем500501*level*502503в корневом логгере. Остальные аргументы интерпретируются так же, как для504505[`debug()`](https://python-all.ru/3.1/library/logging.html#logging.debug)506507.508509#### `logging.disable(lvl)`510511Устанавливает переопределяющий уровень512513*lvl*514515для всех логгеров, который действует в обход собственного уровня логгера. Когда возникает необходимость временно снизить объём вывода сообщений во всём приложении, эта функция может быть полезна. Она отключает все вызовы логирования уровня516517*lvl*518519и ниже, так что если вызвать её со значением INFO, то все события уровня INFO и DEBUG будут отброшены, а события уровня WARNING и выше будут обрабатываться в соответствии с действующим уровнем логгера.520521#### `logging.addLevelName(lvl, levelName)`522523Связывает уровень524525*lvl*526527с текстом528529*levelName*530531во внутреннем словаре, который используется для сопоставления числовых уровней с текстовым представлением, например, когда532533[`Formatter`](https://python-all.ru/3.1/library/logging.html#logging.Formatter)534535форматирует сообщение. Эта функция также может использоваться для определения собственных уровней. Единственные ограничения: все используемые уровни должны быть зарегистрированы с помощью этой функции, уровни должны быть положительными целыми числами и должны возрастать в порядке увеличения серьёзности.536537#### `logging.getLevelName(lvl)`538539Возвращает текстовое представление уровня логирования540541*lvl*542543. Если уровень является одним из предопределённых уровней544545`CRITICAL`546547,548549`ERROR`550551,552553`WARNING`554555,556557`INFO`558559или560561`DEBUG`562563, то возвращается соответствующая строка. Если вы связали уровни с именами с помощью564565[`addLevelName()`](https://python-all.ru/3.1/library/logging.html#logging.addLevelName)566567, то возвращается имя, которое вы связали с568569*lvl*570571. Если передаётся числовое значение, соответствующее одному из определённых уровней, возвращается соответствующее строковое представление. В противном случае возвращается строка «Level %s» % lvl.572573#### `logging.makeLogRecord(attrdict)`574575Создаёт и возвращает новый экземпляр576577[`LogRecord`](https://python-all.ru/3.1/library/logging.html#logging.LogRecord)578579, атрибуты которого определяются580581*attrdict*582583. Эта функция полезна для приёма сериализованного (pickled)584585[`LogRecord`](https://python-all.ru/3.1/library/logging.html#logging.LogRecord)586587словаря атрибутов, отправленного через сокет, и восстановления его как экземпляра588589[`LogRecord`](https://python-all.ru/3.1/library/logging.html#logging.LogRecord)590591на принимающей стороне.592593#### `logging.basicConfig(**kwargs)`594595Выполняет базовую настройку системы журналирования, создавая [`StreamHandler`](https://python-all.ru/3.1/library/logging.html#logging.StreamHandler) с форматтером по умолчанию [`Formatter`](https://python-all.ru/3.1/library/logging.html#logging.Formatter) и добавляя его в корневой логгер. Функции [`debug()`](https://python-all.ru/3.1/library/logging.html#logging.debug), [`info()`](https://python-all.ru/3.1/library/logging.html#logging.info), [`warning()`](https://python-all.ru/3.1/library/logging.html#logging.warning), [`error()`](https://python-all.ru/3.1/library/logging.html#logging.error) и [`critical()`](https://python-all.ru/3.1/library/logging.html#logging.critical) будут автоматически вызывать [`basicConfig()`](https://python-all.ru/3.1/library/logging.html#logging.basicConfig), если для корневого логгера не определено ни одного обработчика.596597Эта функция ничего не делает, если для корневого логгера уже настроены обработчики.598599Поддерживаются следующие аргументы ключевых слов.600601| Формат | Описание |602| --- | --- |603| `filename` | Указывает, что создаётся FileHandler, с использованием указанного имени файла, а не StreamHandler. |604| `filemode` | Задает режим открытия файла, если указан filename (если filemode не указан, по умолчанию используется ‘a’). |605| `format` | Использует указанную строку формата для обработчика. |606| `datefmt` | Использовать указанный формат даты/времени. |607| `level` | Установить уровень корневого регистратора на указанный уровень. |608| `поток данных` | Использовать указанный поток данных для инициализации StreamHandler. Обратите внимание, что этот аргумент несовместим с «filename» – если оба присутствуют, «поток данных» игнорируется. |609610#### `logging.shutdown()`611612Сообщает системе логирования о необходимости выполнить упорядоченное завершение работы, сбрасывая и закрывая все обработчики. Эту функцию следует вызывать при завершении приложения, и после этого вызова не следует использовать систему логирования.613614#### `logging.setLoggerClass(klass)`615616Указывает системе логирования использовать класс617618*klass*619620при создании экземпляра логгера. Класс должен определять621622[`__init__()`](https://python-all.ru/3.1/reference/datamodel.html#object.__init__)623624таким образом, чтобы требовался только аргумент name, а625626[`__init__()`](https://python-all.ru/3.1/reference/datamodel.html#object.__init__)627628должен вызывать629630`Logger.__init__()`631632. Эта функция обычно вызывается до создания любых логгеров приложениями, которым требуется использовать собственное поведение логгера.633634> **См. также**635>636> **[**PEP 282**](https://python-all.ru/3.1/library/logging.html) – Система логирования**637>638> Предложение, описывающее эту возможность для включения в стандартную библиотеку Python.639>640> **[Оригинальный пакет логирования Python](https://python-all.ru/3.1/library/logging.html)**641>642> Это исходный код пакета643>644> `logging`645>646> . Версия, доступная на этом сайте, подходит для Python 1.5.2, 2.1.x и 2.2.x, в которых пакет647>648> `logging`649>650> не входит в стандартную библиотеку.651652## 15.6.5. Объекты Logger653654Логгеры имеют следующие атрибуты и методы. Обратите внимание, что логгеры никогда не создаются напрямую, а всегда через функцию уровня модуля `logging.getLogger(name)`.655656#### `Logger.propagate`657658Если это значение равно false, сообщения логирования не передаются этим логгером или его дочерними логгерами обработчикам логгеров более высокого уровня (предков). Конструктор устанавливает этот атрибут в 1.659660#### `Logger.setLevel(lvl)`661662Устанавливает порог для этого логгера в *lvl*. Сообщения логирования, уровень которых ниже *lvl*, будут игнорироваться. При создании логгера уровень устанавливается в `NOTSET` (что приводит к обработке всех сообщений, если логгер является корневым, или к делегированию родительскому логгеру, если логгер не корневой). Обратите внимание, что корневой логгер создаётся с уровнем `WARNING`.663664Термин «делегирование родителю» означает, что если логгер имеет уровень NOTSET, то просматривается цепочка его логгеров-предков, пока не будет найден предок с уровнем, отличным от NOTSET, или не будет достигнут корневой логгер.665666Если найден предок с уровнем, отличным от NOTSET, то его уровень считается действующим уровнем регистратора, с которого начался поиск предка, и используется для определения обработки события логирования.667668Если достигнут корень и его уровень NOTSET, то будут обработаны все сообщения. В противном случае уровень корня будет использоваться как действующий уровень.669670#### `Logger.isEnabledFor(lvl)`671672Указывает, будет ли сообщение с уровнем серьёзности673674*lvl*675676обработано этим логгером. Этот метод сначала проверяет уровень, установленный на уровне модуля с помощью677678`logging.disable(lvl)`679680, а затем эффективный уровень логгера, определяемый681682[`getEffectiveLevel()`](https://python-all.ru/3.1/library/logging.html#logging.Logger.getEffectiveLevel)683684.685686#### `Logger.getEffectiveLevel()`687688Указывает эффективный уровень для данного логгера. Если с помощью689690[`setLevel()`](https://python-all.ru/3.1/library/logging.html#logging.Logger.setLevel)691692было установлено значение, отличное от693694`NOTSET`695696, возвращается оно. В противном случае выполняется обход иерархии к корню, пока не будет найдено значение, отличное от697698`NOTSET`699700, и возвращается это значение.701702#### `Logger.debug(msg, *args, **kwargs)`703704Записывает сообщение уровня `DEBUG` в этот логгер. *msg* – это строка форматирования сообщения, а *args* – аргументы, которые подставляются в *msg* с помощью оператора форматирования строк. (Обратите внимание, что это означает, что можно использовать ключевые слова в строке форматирования вместе с одним словарным аргументом.)705706В *kwargs* проверяются два именованных аргумента: *exc\_info*, который, если он не равен false, приводит к добавлению информации об исключении в сообщение логирования. Если предоставлен кортеж исключения (в формате, возвращаемом [`sys.exc_info()`](https://python-all.ru/3.1/library/sys.html#sys.exc_info)), он используется; в противном случае вызывается [`sys.exc_info()`](https://python-all.ru/3.1/library/sys.html#sys.exc_info) для получения информации об исключении.707708Другим необязательным именованным аргументом является *extra*, который можно использовать для передачи словаря, которым заполняется \_\_dict\_\_ объекта LogRecord, созданного для события логирования, пользовательскими атрибутами. Затем эти пользовательские атрибуты можно использовать по своему усмотрению. Например, их можно включать в логируемые сообщения. Например:709710```python711FORMAT = "%(asctime)-15s %(clientip)s %(user)-8s %(message)s"712logging.basicConfig(format=FORMAT)713d = { 'clientip' : '192.168.0.1', 'user' : 'fbloggs' }714logger = logging.getLogger("tcpserver")715logger.warning("Protocol problem: %s", "connection reset", extra=d)716```717718выведет что-то вроде719720```python7212006-02-08 22:20:02,165 192.168.0.1 fbloggs Protocol problem: connection reset722```723724Ключи в словаре, переданном в *extra*, не должны пересекаться с ключами, используемыми системой логирования. (См. документацию [`Formatter`](https://python-all.ru/3.1/library/logging.html#logging.Formatter) для получения дополнительной информации о том, какие ключи используются системой логирования.)725726Если вы решите использовать эти атрибуты в логируемых сообщениях, нужно соблюдать осторожность. Например, в приведённом выше примере [`Formatter`](https://python-all.ru/3.1/library/logging.html#logging.Formatter) настроен с форматной строкой, которая ожидает 'clientip' и 'user' в словаре атрибутов LogRecord. Если они отсутствуют, сообщение не будет записано, так как возникнет исключение форматирования строки. Поэтому в этом случае всегда нужно передавать словарь *extra* с этими ключами.727728Хотя это может быть раздражающим, эта функция предназначена для использования в специализированных обстоятельствах, таких как многопоточные серверы, где один и тот же код выполняется во многих контекстах, и интересующие условия зависят от этого контекста (например, IP-адрес удалённого клиента и имя аутентифицированного пользователя в приведённом выше примере). В таких обстоятельствах, скорее всего, будут использоваться специализированные [`Formatter`](https://python-all.ru/3.1/library/logging.html#logging.Formatter) с конкретными `Handler`.729730#### `Logger.info(msg, *args, **kwargs)`731732Записывает сообщение уровня733734`INFO`735736в этот логгер. Аргументы интерпретируются так же, как для737738[`debug()`](https://python-all.ru/3.1/library/logging.html#logging.debug)739740.741742#### `Logger.warning(msg, *args, **kwargs)`743744Записывает сообщение уровня745746`WARNING`747748в этот логгер. Аргументы интерпретируются так же, как для749750[`debug()`](https://python-all.ru/3.1/library/logging.html#logging.debug)751752.753754#### `Logger.error(msg, *args, **kwargs)`755756Записывает сообщение уровня757758`ERROR`759760в этот логгер. Аргументы интерпретируются так же, как для761762[`debug()`](https://python-all.ru/3.1/library/logging.html#logging.debug)763764.765766#### `Logger.critical(msg, *args, **kwargs)`767768Записывает сообщение с уровнем769770`CRITICAL`771772в этот логгер. Аргументы интерпретируются так же, как для773774[`debug()`](https://python-all.ru/3.1/library/logging.html#logging.debug)775776.777778#### `Logger.log(lvl, msg, *args, **kwargs)`779780Записывает сообщение с целочисленным уровнем781782*lvl*783784в этот логгер. Остальные аргументы интерпретируются так же, как для785786[`debug()`](https://python-all.ru/3.1/library/logging.html#logging.debug)787788.789790#### `Logger.exception(msg, *args)`791792Записывает сообщение с уровнем793794`ERROR`795796в этот логгер. Аргументы интерпретируются так же, как для797798[`debug()`](https://python-all.ru/3.1/library/logging.html#logging.debug)799800. Информация об исключении добавляется в сообщение лога. Этот метод следует вызывать только из обработчика исключений.801802#### `Logger.addFilter(filt)`803804Добавляет указанный фильтр805806*filt*807808к этому логгеру.809810#### `Logger.removeFilter(filt)`811812Удаляет указанный фильтр813814*filt*815816из этого логгера.817818#### `Logger.filter(record)`819820Применяет фильтры этого регистратора к записи и возвращает истинное значение, если запись должна быть обработана.821822#### `Logger.addHandler(hdlr)`823824Добавляет указанный обработчик825826*hdlr*827828к этому логгеру.829830#### `Logger.removeHandler(hdlr)`831832Удаляет указанный обработчик833834*hdlr*835836из этого логгера.837838#### `Logger.findCaller()`839840Находит имя исходного файла и номер строки вызывающего кода. Возвращает имя файла, номер строки и имя функции в виде кортежа из трёх элементов.841842#### `Logger.handle(record)`843844Обрабатывает запись, передавая её всем обработчикам, связанным с этим логгером и его предками (пока не будет найдено ложное значение845846*propagate*847848). Этот метод используется для распакованных записей, полученных из сокета, а также для записей, созданных локально. Фильтрация на уровне логгера применяется с помощью849850[`filter()`](https://python-all.ru/3.1/library/logging.html#logging.Logger.filter)851852.853854#### `Logger.makeRecord(name, lvl, fn, lno, msg, args, exc_info, func=None, extra=None)`855856Это фабричный метод, который можно переопределить в подклассах для создания специализированных экземпляров857858[`LogRecord`](https://python-all.ru/3.1/library/logging.html#logging.LogRecord)859860.861862## 15.6.6. Простой пример863864Пакет `logging` предоставляет большую гибкость, и его настройка может показаться пугающей. В этом разделе показано, что простое использование пакета logging вполне возможно.865866Самый простой пример показывает вывод сообщений в консоль:867868```python869import logging870871logging.debug('A debug message')872logging.info('Some information')873logging.warning('A shot across the bows')874```875876Если запустить приведённый выше скрипт, результат будет таким:877878```python879WARNING:root:A shot across the bows880```881882Поскольку не был указан конкретный регистратор, система использовала корневой регистратор. Сообщения уровней debug и info не появились, потому что по умолчанию корневой регистратор настроен на обработку только сообщений с уровнем WARNING и выше. Формат сообщений также задаётся по умолчанию, как и место вывода сообщений – `sys.stderr`. Уровень, формат и место вывода можно легко изменить, как показано в примере ниже:883884```python885import logging886887logging.basicConfig(level=logging.DEBUG,888 format='%(asctime)s %(levelname)s %(message)s',889 filename='myapp.log',890 filemode='w')891logging.debug('A debug message')892logging.info('Some information')893logging.warning('A shot across the bows')894```895896Метод [`basicConfig()`](https://python-all.ru/3.1/library/logging.html#logging.basicConfig) используется для изменения настроек по умолчанию, в результате чего вывод (записанный в `myapp.log`) будет выглядеть примерно так:897898```python8992004-07-02 13:00:08,743 DEBUG A debug message9002004-07-02 13:00:08,743 INFO Some information9012004-07-02 13:00:08,743 WARNING A shot across the bows902```903904На этот раз были обработаны все сообщения с уровнем DEBUG и выше, был изменён формат сообщений, а вывод пошёл в указанный файл, а не в консоль.905906Форматирование использует старый стиль форматирования строк Python – см. раздел [*Старые операции форматирования строк*](https://python-all.ru/3.1/library/stdtypes.html#old-string-formatting). Строка формата принимает следующие общие спецификаторы. Полный список спецификаторов можно найти в документации [`Formatter`](https://python-all.ru/3.1/library/logging.html#logging.Formatter).907908| Формат | Описание |909| --- | --- |910| `%(name)s` | Имя регистратора (канал журналирования). |911| `%(levelname)s` | Текстовый уровень логирования сообщения (`'DEBUG'`, `'INFO'`, `'WARNING'`, `'ERROR'`, `'CRITICAL'`). |912| `%(asctime)s` | Человекочитаемое время создания [`LogRecord`](https://python-all.ru/3.1/library/logging.html#logging.LogRecord). По умолчанию имеет вид «2003-07-08 16:49:45,896» (цифры после запятой – миллисекунды). |913| `%(message)s` | Записанное сообщение. |914915Чтобы изменить формат даты/времени, можно передать дополнительный именованный параметр *datefmt*, как показано ниже:916917```python918import logging919920logging.basicConfig(level=logging.DEBUG,921 format='%(asctime)s %(levelname)-8s %(message)s',922 datefmt='%a, %d %b %Y %H:%M:%S',923 filename='/temp/myapp.log',924 filemode='w')925logging.debug('A debug message')926logging.info('Some information')927logging.warning('A shot across the bows')928```929930в результате чего вывод будет выглядеть как931932```python933Fri, 02 Jul 2004 13:06:18 DEBUG A debug message934Fri, 02 Jul 2004 13:06:18 INFO Some information935Fri, 02 Jul 2004 13:06:18 WARNING A shot across the bows936```937938Строка формата даты соответствует требованиям `strftime()` – см. документацию модуля [`time`](https://python-all.ru/3.1/library/time.html#module-time).939940Если вместо отправки вывода журнала в консоль или файл вы хотите использовать объект, подобный файлу, созданный отдельно, вы можете передать его в [`basicConfig()`](https://python-all.ru/3.1/library/logging.html#logging.basicConfig) с помощью именованного аргумента *stream*. Обратите внимание, что если переданы оба именованных аргумента *stream* и *filename*, аргумент *stream* игнорируется.941942Конечно, вы можете включать переменную информацию в вывод. Для этого просто сделайте сообщение строкой формата и передайте дополнительные аргументы, содержащие переменную информацию, как в следующем примере:943944```python945import logging946947logging.basicConfig(level=logging.DEBUG,948 format='%(asctime)s %(levelname)-8s %(message)s',949 datefmt='%a, %d %b %Y %H:%M:%S',950 filename='/temp/myapp.log',951 filemode='w')952logging.error('Pack my box with %d dozen %s', 5, 'liquor jugs')953```954955в результате чего получится956957```python958Wed, 21 Jul 2004 15:35:16 ERROR Pack my box with 5 dozen liquor jugs959```960961## 15.6.7. Логирование в несколько мест назначения962963Допустим, вы хотите вести журнал в консоль и файл с разными форматами сообщений и в разных ситуациях. Предположим, вы хотите записывать сообщения уровня DEBUG и выше в файл, а сообщения уровня INFO и выше – в консоль. Также предположим, что файл должен содержать временные метки, а консольные сообщения – нет. Вот как это можно сделать:964965```python966import logging967968# настроить логирование в файл – подробнее см. предыдущий раздел969logging.basicConfig(level=logging.DEBUG,970 format='%(asctime)s %(name)-12s %(levelname)-8s %(message)s',971 datefmt='%m-%d %H:%M',972 filename='/temp/myapp.log',973 filemode='w')974# Определяет обработчик, который записывает сообщения уровня INFO и выше в sys.stderr.975console = logging.StreamHandler()976console.setLevel(logging.INFO)977# Задает формат, упрощенный для вывода в консоль.978formatter = logging.Formatter('%(name)-12s: %(levelname)-8s %(message)s')979# Указывает обработчику использовать этот формат.980console.setFormatter(formatter)981# Добавляет обработчик в корневой логгер.982logging.getLogger('').addHandler(console)983984# Теперь можно выполнять запись в корневой логгер или любой другой. Сначала корневой...985logging.info('Jackdaws love my big sphinx of quartz.')986987# Теперь определите несколько других логгеров, которые могут представлять разные части приложения:988# приложение:989990logger1 = logging.getLogger('myapp.area1')991logger2 = logging.getLogger('myapp.area2')992993logger1.debug('Quick zephyrs blow, vexing daft Jim.')994logger1.info('How quickly daft jumping zebras vex.')995logger2.warning('Jail zesty vixen who grabbed pay from quack.')996logger2.error('The five boxing wizards jump quickly.')997```998999При запуске на консоли вы увидите10001001```python1002root : INFO Jackdaws love my big sphinx of quartz.1003myapp.area1 : INFO How quickly daft jumping zebras vex.1004myapp.area2 : WARNING Jail zesty vixen who grabbed pay from quack.1005myapp.area2 : ERROR The five boxing wizards jump quickly.1006```10071008а в файле увидите примерно такое10091010```python101110-22 22:19 root INFO Jackdaws love my big sphinx of quartz.101210-22 22:19 myapp.area1 DEBUG Quick zephyrs blow, vexing daft Jim.101310-22 22:19 myapp.area1 INFO How quickly daft jumping zebras vex.101410-22 22:19 myapp.area2 WARNING Jail zesty vixen who grabbed pay from quack.101510-22 22:19 myapp.area2 ERROR The five boxing wizards jump quickly.1016```10171018Как видите, сообщение DEBUG отображается только в файле. Остальные сообщения отправляются в оба места назначения.10191020В этом примере используются консольный и файловый обработчики, но вы можете использовать любое количество и любое сочетание обработчиков по своему выбору.10211022## 15.6.8. Исключения, возникшие во время логирования10231024Пакет logging спроектирован таким образом, чтобы проглатывать исключения, возникающие при журналировании в рабочей среде. Это делается для того, чтобы ошибки, возникающие при обработке событий журналирования – такие как неверная конфигурация журналирования, сетевые или другие подобные ошибки – не приводили к преждевременному завершению приложения, использующего журналирование.10251026Исключения [`SystemExit`](https://python-all.ru/3.1/library/exceptions.html#SystemExit) и [`KeyboardInterrupt`](https://python-all.ru/3.1/library/exceptions.html#KeyboardInterrupt) никогда не поглощаются. Другие исключения, возникающие во время выполнения метода `emit()` подкласса `Handler`, передаются его методу `handleError()`.10271028Реализация по умолчанию метода `handleError()` в `Handler` проверяет, установлена ли переменная уровня модуля `raiseExceptions`. Если установлена, трассировка выводится в [`sys.stderr`](https://python-all.ru/3.1/library/sys.html#sys.stderr). Если не установлена, исключение поглощается.10291030**Примечание:** Значение по умолчанию `raiseExceptions` – `True`. Это потому, что во время разработки обычно хочется получать уведомления о любых исключениях. Рекомендуется установить `raiseExceptions` в `False` для использования в production.10311032## 15.6.9. Добавление контекстной информации в вывод логирования10331034Иногда требуется, чтобы вывод логирования содержал контекстную информацию в дополнение к параметрам, переданным в вызов логирования. Например, в сетевом приложении может быть желательно регистрировать специфичную для клиента информацию в журнале (например, имя пользователя удаленного клиента или IP-адрес). Хотя вы можете использовать параметр *extra* для этого, не всегда удобно передавать информацию таким образом. Хотя может возникнуть соблазн создавать экземпляры `Logger` для каждого соединения, это не очень хорошая идея, потому что эти экземпляры не собираются сборщиком мусора. Хотя на практике это не проблема, когда количество экземпляров `Logger` зависит от уровня детализации, которую вы хотите использовать при логировании приложения, это может быть трудно управляемым, если количество экземпляров `Logger` становится фактически неограниченным.10351036### 15.6.9.1. Использование LoggerAdapter для передачи контекстной информации10371038Простой способ передавать контекстную информацию для вывода вместе с событием логирования – использовать класс [`LoggerAdapter`](https://python-all.ru/3.1/library/logging.html#logging.LoggerAdapter). Этот класс устроен так, что выглядит как `Logger`, поэтому можно вызывать [`debug()`](https://python-all.ru/3.1/library/logging.html#logging.debug), [`info()`](https://python-all.ru/3.1/library/logging.html#logging.info), [`warning()`](https://python-all.ru/3.1/library/logging.html#logging.warning), [`error()`](https://python-all.ru/3.1/library/logging.html#logging.error), [`exception()`](https://python-all.ru/3.1/library/logging.html#logging.exception), [`critical()`](https://python-all.ru/3.1/library/logging.html#logging.critical) и [`log()`](https://python-all.ru/3.1/library/logging.html#logging.log). Сигнатуры этих методов совпадают с их аналогами в `Logger`, так что экземпляры обоих типов можно использовать взаимозаменяемо.10391040При создании экземпляра [`LoggerAdapter`](https://python-all.ru/3.1/library/logging.html#logging.LoggerAdapter) ему передаются экземпляр `Logger` и объект, подобный словарю, содержащий контекстную информацию. При вызове любого из методов логирования у экземпляра [`LoggerAdapter`](https://python-all.ru/3.1/library/logging.html#logging.LoggerAdapter) он делегирует вызов нижележащему экземпляру `Logger`, переданному в его конструктор, и обеспечивает передачу контекстной информации в делегированном вызове. Вот фрагмент кода [`LoggerAdapter`](https://python-all.ru/3.1/library/logging.html#logging.LoggerAdapter):10411042```python1043def debug(self, msg, *args, **kwargs):1044 """1045 Передаёт вызов отладки нижележащему регистратору после добавления1046 контекстной информации из данного экземпляра адаптера.1047 """1048 msg, kwargs = self.process(msg, kwargs)1049 self.logger.debug(msg, *args, **kwargs)1050```10511052Метод `process()` класса [`LoggerAdapter`](https://python-all.ru/3.1/library/logging.html#logging.LoggerAdapter) – это место, где контекстная информация добавляется в вывод логирования. Ему передаются сообщение и именованные аргументы вызова логирования, и он возвращает (возможно) изменённые версии этих данных для использования в вызове нижележащего логгера. В реализации по умолчанию этот метод оставляет сообщение без изменений, но добавляет ключ «extra» в именованные аргументы, значением которого является объект, подобный словарю, переданный в конструктор. Разумеется, если в вызове адаптера уже был передан именованный аргумент «extra», он будет молча перезаписан.10531054Преимущество использования «extra» в том, что значения из объекта, подобного словарю, сливаются в [`LogRecord`](https://python-all.ru/3.1/library/logging.html#logging.LogRecord).\_\_dict\_\_, что позволяет использовать настроенные строки с экземплярами [`Formatter`](https://python-all.ru/3.1/library/logging.html#logging.Formatter), которым известны ключи этого объекта. Если нужен другой подход, например, требуется добавить контекстную информацию в начало или конец строки сообщения, достаточно создать подкласс [`LoggerAdapter`](https://python-all.ru/3.1/library/logging.html#logging.LoggerAdapter) и переопределить `process()`, чтобы он делал то, что нужно. Вот пример скрипта, использующего этот класс, который также демонстрирует, какое поведение, подобное словарю, требуется от произвольного «dict-like» объекта для использования в конструкторе:10551056```python1057import logging10581059class ConnInfo:1060 """1061 Пример класса, который показывает, как произвольный класс можно использовать в качестве1062 хранилища контекстной информации 'extra', передаваемого LoggerAdapter.1063 """10641065 def __getitem__(self, name):1066 """1067 Чтобы этот экземпляр выглядел как словарь.1068 """1069 from random import choice1070 if name == "ip":1071 result = choice(["127.0.0.1", "192.168.0.1"])1072 elif name == "user":1073 result = choice(["jim", "fred", "sheila"])1074 else:1075 result = self.__dict__.get(name, "?")1076 return result10771078 def __iter__(self):1079 """1080 Чтобы разрешить итерацию по ключам, которые будут объединены в1081 словарь LogRecord перед форматированием и выводом.1082 """1083 keys = ["ip", "user"]1084 keys.extend(self.__dict__.keys())1085 return keys.__iter__()10861087if __name__ == "__main__":1088 from random import choice1089 levels = (logging.DEBUG, logging.INFO, logging.WARNING, logging.ERROR, logging.CRITICAL)1090 a1 = logging.LoggerAdapter(logging.getLogger("a.b.c"),1091 { "ip" : "123.231.231.123", "user" : "sheila" })1092 logging.basicConfig(level=logging.DEBUG,1093 format="%(asctime)-15s %(name)-5s %(levelname)-8s IP: %(ip)-15s User: %(user)-8s %(message)s")1094 a1.debug("A debug message")1095 a1.info("An info message with %s", "some parameters")1096 a2 = logging.LoggerAdapter(logging.getLogger("d.e.f"), ConnInfo())1097 for x in range(10):1098 lvl = choice(levels)1099 lvlname = logging.getLevelName(lvl)1100 a2.log(lvl, "A message at %s level with %d %s", lvlname, 2, "parameters")1101```11021103При запуске этого скрипта вывод должен выглядеть примерно так:11041105```python11062008-01-18 14:49:54,023 a.b.c DEBUG IP: 123.231.231.123 User: sheila A debug message11072008-01-18 14:49:54,023 a.b.c INFO IP: 123.231.231.123 User: sheila An info message with some parameters11082008-01-18 14:49:54,023 d.e.f CRITICAL IP: 192.168.0.1 User: jim A message at CRITICAL level with 2 parameters11092008-01-18 14:49:54,033 d.e.f INFO IP: 192.168.0.1 User: jim A message at INFO level with 2 parameters11102008-01-18 14:49:54,033 d.e.f WARNING IP: 192.168.0.1 User: sheila A message at WARNING level with 2 parameters11112008-01-18 14:49:54,033 d.e.f ERROR IP: 127.0.0.1 User: fred A message at ERROR level with 2 parameters11122008-01-18 14:49:54,033 d.e.f ERROR IP: 127.0.0.1 User: sheila A message at ERROR level with 2 parameters11132008-01-18 14:49:54,033 d.e.f WARNING IP: 192.168.0.1 User: sheila A message at WARNING level with 2 parameters11142008-01-18 14:49:54,033 d.e.f WARNING IP: 192.168.0.1 User: jim A message at WARNING level with 2 parameters11152008-01-18 14:49:54,033 d.e.f INFO IP: 192.168.0.1 User: fred A message at INFO level with 2 parameters11162008-01-18 14:49:54,033 d.e.f WARNING IP: 192.168.0.1 User: sheila A message at WARNING level with 2 parameters11172008-01-18 14:49:54,033 d.e.f WARNING IP: 127.0.0.1 User: jim A message at WARNING level with 2 parameters1118```11191120### 15.6.9.2. Использование фильтров для передачи контекстной информации11211122Контекстную информацию можно также добавлять в вывод логирования с помощью определённого пользователем фильтра [`Filter`](https://python-all.ru/3.1/library/logging.html#logging.Filter). Экземпляры `Filter` могут изменять `LogRecords`, переданные им, в том числе добавляя дополнительные атрибуты, которые затем можно вывести с помощью подходящей форматной строки или, при необходимости, собственного [`Formatter`](https://python-all.ru/3.1/library/logging.html#logging.Formatter).11231124Например, в веб-приложении обрабатываемый запрос (или, по крайней мере, его интересные части) может храниться в переменной threadlocal ([`threading.local`](https://python-all.ru/3.1/library/threading.html#threading.local)), а затем извлекаться из `Filter` для добавления, скажем, информации из запроса – например, удаленного IP-адреса и имени пользователя – в `LogRecord`, используя имена атрибутов 'ip' и 'user', как в примере с `LoggerAdapter` выше. В этом случае можно использовать ту же строку формата для получения аналогичного вывода, показанного выше. Вот пример скрипта:11251126```python1127import logging1128from random import choice11291130class ContextFilter(logging.Filter):1131 """1132 Это фильтр, который внедряет контекстную информацию в журнал.11331134 Вместо использования реальной контекстной информации в данном примере используются случайные1135 данные.1136 """11371138 USERS = ['jim', 'fred', 'sheila']1139 IPS = ['123.231.231.123', '127.0.0.1', '192.168.0.1']11401141 def filter(self, record):11421143 record.ip = choice(ContextFilter.IPS)1144 record.user = choice(ContextFilter.USERS)1145 return True11461147if __name__ == "__main__":1148 levels = (logging.DEBUG, logging.INFO, logging.WARNING, logging.ERROR, logging.CRITICAL)1149 a1 = logging.LoggerAdapter(logging.getLogger("a.b.c"),1150 { "ip" : "123.231.231.123", "user" : "sheila" })1151 logging.basicConfig(level=logging.DEBUG,1152 format="%(asctime)-15s %(name)-5s %(levelname)-8s IP: %(ip)-15s User: %(user)-8s %(message)s")1153 a1 = logging.getLogger("a.b.c")1154 a2 = logging.getLogger("d.e.f")11551156 f = ContextFilter()1157 a1.addFilter(f)1158 a2.addFilter(f)1159 a1.debug("A debug message")1160 a1.info("An info message with %s", "some parameters")1161 for x in range(10):1162 lvl = choice(levels)1163 lvlname = logging.getLevelName(lvl)1164 a2.log(lvl, "A message at %s level with %d %s", lvlname, 2, "parameters")1165```11661167который при запуске выдаёт примерно следующее:11681169```python11702010-09-06 22:38:15,292 a.b.c DEBUG IP: 123.231.231.123 User: fred A debug message11712010-09-06 22:38:15,300 a.b.c INFO IP: 192.168.0.1 User: sheila An info message with some parameters11722010-09-06 22:38:15,300 d.e.f CRITICAL IP: 127.0.0.1 User: sheila A message at CRITICAL level with 2 parameters11732010-09-06 22:38:15,300 d.e.f ERROR IP: 127.0.0.1 User: jim A message at ERROR level with 2 parameters11742010-09-06 22:38:15,300 d.e.f DEBUG IP: 127.0.0.1 User: sheila A message at DEBUG level with 2 parameters11752010-09-06 22:38:15,300 d.e.f ERROR IP: 123.231.231.123 User: fred A message at ERROR level with 2 parameters11762010-09-06 22:38:15,300 d.e.f CRITICAL IP: 192.168.0.1 User: jim A message at CRITICAL level with 2 parameters11772010-09-06 22:38:15,300 d.e.f CRITICAL IP: 127.0.0.1 User: sheila A message at CRITICAL level with 2 parameters11782010-09-06 22:38:15,300 d.e.f DEBUG IP: 192.168.0.1 User: jim A message at DEBUG level with 2 parameters11792010-09-06 22:38:15,301 d.e.f ERROR IP: 127.0.0.1 User: sheila A message at ERROR level with 2 parameters11802010-09-06 22:38:15,301 d.e.f DEBUG IP: 123.231.231.123 User: fred A message at DEBUG level with 2 parameters11812010-09-06 22:38:15,301 d.e.f INFO IP: 123.231.231.123 User: fred A message at INFO level with 2 parameters1182```11831184## 15.6.10. Логирование в один файл из нескольких процессов11851186Хотя логирование потокобезопасно, и логирование в один файл из нескольких потоков в одном процессе *поддерживается*, логирование в один файл из *нескольких процессов* *не* поддерживается, поскольку в Python нет стандартного способа сериализовать доступ к одному файлу из нескольких процессов. Если необходимо вести логирование в один файл из нескольких процессов, один из способов – настроить все процессы на отправку логов в `SocketHandler` и запустить отдельный процесс, реализующий сокет-сервер, который читает данные из сокета и пишет их в файл. (При желании можно выделить один поток в одном из существующих процессов для выполнения этой функции.) В следующем разделе этот подход описан подробнее, включая рабочий сокет-приёмник, который можно использовать как отправную точку для адаптации в собственных приложениях.11871188Если вы используете свежую версию Python, включающую модуль [`multiprocessing`](https://python-all.ru/3.1/library/multiprocessing.html#module-multiprocessing), можно написать собственный обработчик, который использует класс блокировки `Lock` из этого модуля для сериализации доступа к файлу из ваших процессов. Существующие [`FileHandler`](https://python-all.ru/3.1/library/logging.html#logging.FileHandler) и его подклассы в настоящее время не используют [`multiprocessing`](https://python-all.ru/3.1/library/multiprocessing.html#module-multiprocessing), хотя в будущем могут начать. Обратите внимание: на данный момент модуль [`multiprocessing`](https://python-all.ru/3.1/library/multiprocessing.html#module-multiprocessing) не предоставляет работающей функциональности блокировок на всех платформах (см. [http://bugs.python.org/issue3770](https://python-all.ru/3.1/library/logging.html)).11891190## 15.6.11. Отправка и получение событий логирования по сети11911192Допустим, требуется отправлять события логирования по сети и обрабатывать их на принимающей стороне. Простой способ – прикрепить экземпляр `SocketHandler` к корневому логгеру на отправляющей стороне:11931194```python1195import logging, logging.handlers11961197rootLogger = logging.getLogger('')1198rootLogger.setLevel(logging.DEBUG)1199socketHandler = logging.handlers.SocketHandler('localhost',1200 logging.handlers.DEFAULT_TCP_LOGGING_PORT)1201# Не нужно использовать форматтер, так как сокет-обработчик отправляет событие в виде неформатированного пикла.1202# неформатированный pickle1203rootLogger.addHandler(socketHandler)12041205# Теперь можно выполнять запись в корневой логгер или любой другой. Сначала корневой...1206logging.info('Jackdaws love my big sphinx of quartz.')12071208# Теперь определите несколько других логгеров, которые могут представлять разные части приложения:1209# приложение:12101211logger1 = logging.getLogger('myapp.area1')1212logger2 = logging.getLogger('myapp.area2')12131214logger1.debug('Quick zephyrs blow, vexing daft Jim.')1215logger1.info('How quickly daft jumping zebras vex.')1216logger2.warning('Jail zesty vixen who grabbed pay from quack.')1217logger2.error('The five boxing wizards jump quickly.')1218```12191220На принимающей стороне можно настроить приемник с помощью модуля [`socketserver`](https://python-all.ru/3.1/library/socketserver.html#module-socketserver). Вот базовый рабочий пример:12211222```python1223import pickle1224import logging1225import logging.handlers1226import socketserver1227import struct12281229class LogRecordStreamHandler(socketserver.StreamRequestHandler):1230 """Обработчик для потокового запроса логирования.12311232 Это по сути записывает запись, используя ту политику логирования, которая настроена локально.1233 настроено локально.1234 """12351236 def handle(self):1237 """1238 Обрабатывает несколько запросов – каждый ожидается в формате: 4-байтовая длина, затем запись журнала в формате пикл.1239 Записывает запись в соответствии с политикой, настроенной локально.1240 согласно локально настроенной политике.1241 """1242 while True:1243 chunk = self.connection.recv(4)1244 if len(chunk) < 4:1245 break1246 slen = struct.unpack(">L", chunk)[0]1247 chunk = self.connection.recv(slen)1248 while len(chunk) < slen:1249 chunk = chunk + self.connection.recv(slen - len(chunk))1250 obj = self.unPickle(chunk)1251 record = logging.makeLogRecord(obj)1252 self.handleLogRecord(record)12531254 def unPickle(self, data):1255 return pickle.loads(data)12561257 def handleLogRecord(self, record):1258 # Если указано имя, используется именованный логгер, а не тот, что1259 # подразумевается записью.1260 if self.server.logname is not None:1261 name = self.server.logname1262 else:1263 name = record.name1264 logger = logging.getLogger(name)1265 # Примечание: КАЖДАЯ запись попадает в журнал. Это связано с тем, что Logger.handle1266 # обычно вызывается ПОСЛЕ фильтрации на уровне регистратора. Если требуется1267 # выполнять фильтрацию, делайте это на стороне клиента, чтобы не тратить1268 # циклы процессора и сетевую пропускную способность!1269 logger.handle(record)12701271class LogRecordSocketReceiver(socketserver.ThreadingTCPServer):1272 """простой приёмник логов на основе TCP-сокетов, подходящий для тестирования.1273 """12741275 allow_reuse_address = 112761277 def __init__(self, host='localhost',1278 port=logging.handlers.DEFAULT_TCP_LOGGING_PORT,1279 handler=LogRecordStreamHandler):1280 socketserver.ThreadingTCPServer.__init__(self, (host, port), handler)1281 self.abort = 01282 self.timeout = 11283 self.logname = None12841285 def serve_until_stopped(self):1286 import select1287 abort = 01288 while not abort:1289 rd, wr, ex = select.select([self.socket.fileno()],1290 [], [],1291 self.timeout)1292 if rd:1293 self.handle_request()1294 abort = self.abort12951296def main():1297 logging.basicConfig(1298 format="%(relativeCreated)5d %(name)-15s %(levelname)-8s %(message)s")1299 tcpserver = LogRecordSocketReceiver()1300 print("About to start TCP server...")1301 tcpserver.serve_until_stopped()13021303if __name__ == "__main__":1304 main()1305```13061307Сначала запустите сервер, затем клиент. На стороне клиента в консоль ничего не выводится; на стороне сервера вы должны увидеть что-то вроде:13081309```python1310About to start TCP server...1311 59 root INFO Jackdaws love my big sphinx of quartz.1312 59 myapp.area1 DEBUG Quick zephyrs blow, vexing daft Jim.1313 69 myapp.area1 INFO How quickly daft jumping zebras vex.1314 69 myapp.area2 WARNING Jail zesty vixen who grabbed pay from quack.1315 69 myapp.area2 ERROR The five boxing wizards jump quickly.1316```13171318Обратите внимание, что в некоторых сценариях использование pickle связано с рисками безопасности. Если это вас касается, вы можете использовать альтернативную схему сериализации, переопределив метод `makePickle()` и реализовав свою альтернативу в нём, а также адаптировав приведённый выше скрипт для работы с вашей сериализацией.13191320## 15.6.12. Использование произвольных объектов в качестве сообщений13211322В предыдущих разделах и примерах предполагалось, что сообщение, передаваемое при регистрации события, является строкой. Однако это не единственная возможность. Можно передать в качестве сообщения произвольный объект, и его метод [`__str__()`](https://python-all.ru/3.1/reference/datamodel.html#object.__str__) будет вызван, когда системе логирования потребуется преобразовать его в строковое представление. Более того, при желании можно вообще избежать вычисления строкового представления – например, `SocketHandler` отправляет событие, сериализуя его и передавая по сети.13231324## 15.6.13. Оптимизация13251326Форматирование аргументов сообщения откладывается до тех пор, пока его нельзя избежать. Однако вычисление аргументов, передаваемых методу логирования, также может быть дорогостоящим, и, возможно, захочется избежать этого, если логгер всё равно отбросит событие. Чтобы решить, что делать, можно вызвать метод `isEnabledFor()`, который принимает аргумент уровня и возвращает true, если Logger создаст событие для этого уровня вызова. Можно написать такой код:13271328```python1329if logger.isEnabledFor(logging.DEBUG):1330 logger.debug("Message with %s, %s", expensive_func1(),1331 expensive_func2())1332```13331334чтобы, если порог регистратора установлен выше `DEBUG`, вызовы `expensive_func1()` и `expensive_func2()` никогда не выполнялись.13351336Существуют и другие оптимизации, которые можно выполнить для конкретных приложений, требующих более точного контроля над тем, какая информация логирования собирается. Вот список действий, которые можно предпринять, чтобы избежать ненужной обработки во время логирования:13371338| Что не нужно собирать | Как избежать сбора |1339| --- | --- |1340| Информация о том, откуда были сделаны вызовы. | Установить `logging._srcfile` в `None`. |1341| Информация о потоках. | Установите `logging.logThreads` в `0`. |1342| Информация о процессе. | Установите `logging.logProcesses` в `0`. |13431344Также обратите внимание, что основной модуль logging включает только базовые обработчики. Если не импортировать `logging.handlers` и `logging.config`, они не будут занимать память.13451346## 15.6.14. Объекты-обработчики13471348Обработчики имеют следующие атрибуты и методы. Обратите внимание, что `Handler` никогда не создаётся напрямую; этот класс служит базой для более полезных подклассов. Однако метод [`__init__()`](https://python-all.ru/3.1/reference/datamodel.html#object.__init__) в подклассах должен вызывать [`Handler.__init__()`](https://python-all.ru/3.1/library/logging.html#logging.Handler.__init__).13491350#### `Handler.__init__(level=NOTSET)`13511352Инициализирует экземпляр13531354`Handler`13551356, устанавливая его уровень, устанавливая список фильтров в пустой список и создавая блокировку (с помощью13571358[`createLock()`](https://python-all.ru/3.1/library/logging.html#logging.Handler.createLock)13591360) для сериализации доступа к механизму ввода-вывода.13611362#### `Handler.createLock()`13631364Инициализирует блокировку потока, которую можно использовать для сериализации доступа к нижележащей функциональности ввода-вывода, которая может не быть потокобезопасной.13651366#### `Handler.acquire()`13671368Захватывает блокировку потока, созданную с помощью13691370[`createLock()`](https://python-all.ru/3.1/library/logging.html#logging.Handler.createLock)13711372.13731374#### `Handler.release()`13751376Освобождает блокировку потока, полученную с помощью13771378[`acquire()`](https://python-all.ru/3.1/library/logging.html#logging.Handler.acquire)13791380.13811382#### `Handler.setLevel(lvl)`13831384Устанавливает порог для этого обработчика в13851386*lvl*13871388. Сообщения журнала, которые менее серьезны, чем13891390*lvl*13911392, будут игнорироваться. При создании обработчика уровень устанавливается в13931394`NOTSET`13951396(из-за чего обрабатываются все сообщения).13971398#### `Handler.setFormatter(form)`13991400Устанавливает14011402[`Formatter`](https://python-all.ru/3.1/library/logging.html#logging.Formatter)14031404для этого обработчика в14051406*form*14071408.14091410#### `Handler.addFilter(filt)`14111412Добавляет указанный фильтр14131414*фильтр*14151416в данный обработчик.14171418#### `Handler.removeFilter(filt)`14191420Удаляет указанный фильтр14211422*фильтр*14231424из данного обработчика.14251426#### `Handler.filter(record)`14271428Применяет фильтры этого обработчика к записи и возвращает true, если запись должна быть обработана.14291430#### `Handler.flush()`14311432Обеспечивает сброс всего вывода логирования. Данная версия ничего не делает и предназначена для реализации в подклассах.14331434#### `Handler.close()`14351436Освобождает все ресурсы, используемые обработчиком. Эта версия не производит вывод, но удаляет обработчик из внутреннего списка обработчиков, который закрывается при вызове14371438[`shutdown()`](https://python-all.ru/3.1/library/logging.html#logging.shutdown)14391440. Подклассы должны гарантировать, что этот метод вызывается из переопределенных методов14411442[`close()`](https://python-all.ru/3.1/library/logging.html#logging.Handler.close)14431444.14451446#### `Handler.handle(record)`14471448Условно выдает указанную запись логирования в зависимости от фильтров, которые могут быть добавлены к обработчику. Оборачивает фактическую выдачу записи в захват/освобождение блокировки потока ввода-вывода.14491450#### `Handler.handleError(record)`14511452Этот метод следует вызывать из обработчиков при возникновении исключения во время вызова14531454[`emit()`](https://python-all.ru/3.1/library/logging.html#logging.Handler.emit)14551456. По умолчанию он ничего не делает, то есть исключения молча игнорируются. В системе логирования это в основном то, что нужно – большинство пользователей не интересуются ошибками в системе логирования, их больше волнуют ошибки приложения. Однако при желании можно заменить этот метод собственным обработчиком. Указанная запись (record) – это та, которая обрабатывалась в момент возникновения исключения.14571458#### `Handler.format(record)`14591460Форматирует запись – если задан форматировщик, использует его. В противном случае использует форматировщик по умолчанию для модуля.14611462#### `Handler.emit(record)`14631464Делает все необходимое для фактической записи указанной записи журнала. Эта версия предназначена для реализации в подклассах и поэтому вызывает14651466[`NotImplementedError`](https://python-all.ru/3.1/library/exceptions.html#NotImplementedError)14671468.14691470### 15.6.14.1. StreamHandler14711472Класс [`StreamHandler`](https://python-all.ru/3.1/library/logging.html#logging.StreamHandler), находящийся в основном пакете `logging`, направляет вывод логирования в потоки, такие как *sys.stdout*, *sys.stderr* или любой объект, похожий на файл (точнее, любой объект, поддерживающий методы `write()` и `flush()`).14731474#### `class logging.StreamHandler(stream=None)`14751476Возвращает новый экземпляр класса [`StreamHandler`](https://python-all.ru/3.1/library/logging.html#logging.StreamHandler). Если указан параметр *поток данных*, экземпляр использует его для вывода логирования; в противном случае используется *sys.stderr*.14771478#### `emit(record)`14791480Если задан форматтер, он используется для форматирования записи. Затем запись записывается в поток с завершающим символом новой строки. Если присутствует информация об исключении, она форматируется с помощью14811482[`traceback.print_exception()`](https://python-all.ru/3.1/library/traceback.html#traceback.print_exception)14831484и добавляется в поток.14851486#### `flush()`14871488Сбрасывает поток данных, вызывая его метод14891490[`flush()`](https://python-all.ru/3.1/library/logging.html#logging.StreamHandler.flush)14911492. Обратите внимание, что метод14931494`close()`14951496наследуется от14971498`Handler`14991500и поэтому не выводит ничего, так что иногда может потребоваться явный вызов15011502[`flush()`](https://python-all.ru/3.1/library/logging.html#logging.StreamHandler.flush)15031504.15051506### 15.6.14.2. FileHandler15071508Класс [`FileHandler`](https://python-all.ru/3.1/library/logging.html#logging.FileHandler), находящийся в основном пакете `logging`, направляет вывод логирования в файл на диске. Он наследует функциональность вывода от [`StreamHandler`](https://python-all.ru/3.1/library/logging.html#logging.StreamHandler).15091510#### `class logging.FileHandler(filename, mode='a', encoding=None, delay=0)`15111512Возвращает новый экземпляр класса [`FileHandler`](https://python-all.ru/3.1/library/logging.html#logging.FileHandler). Указанный файл открывается и используется как поток для логирования. Если *mode* не указан, используется `'a'`. Если *encoding* не равен *None*, файл открывается с указанной кодировкой. Если *delay* равен true, открытие файла откладывается до первого вызова [`emit()`](https://python-all.ru/3.1/library/logging.html#logging.FileHandler.emit). По умолчанию файл растёт бесконечно.15131514#### `close()`15151516Закрывает файл.15171518#### `emit(record)`15191520Выводит запись в файл.15211522### 15.6.14.3. NullHandler15231524Новое в версии 3.1.15251526Класс [`NullHandler`](https://python-all.ru/3.1/library/logging.html#logging.NullHandler), находящийся в основном пакете `logging`, не выполняет никакого форматирования или вывода. По сути, это обработчик-пустышка, предназначенный для разработчиков библиотек.15271528#### `class logging.NullHandler`15291530Возвращает новый экземпляр класса [`NullHandler`](https://python-all.ru/3.1/library/logging.html#logging.NullHandler).15311532#### `emit(record)`15331534Этот метод ничего не делает.15351536Смотрите [*Настройка логирования для библиотеки*](https://python-all.ru/3.1/library/logging.html#library-config) для получения дополнительной информации об использовании [`NullHandler`](https://python-all.ru/3.1/library/logging.html#logging.NullHandler).15371538### 15.6.14.4. WatchedFileHandler15391540Класс [`WatchedFileHandler`](https://python-all.ru/3.1/library/logging.html#logging.handlers.WatchedFileHandler), находящийся в модуле `logging.handlers`, представляет собой `FileHandler`, который следит за файлом, в который ведётся логирование. Если файл изменяется, он закрывается и открывается заново по тому же имени файла.15411542Файл может измениться из-за использования таких программ, как *newsyslog* и *logrotate*, которые выполняют ротацию файлов журнала. Этот обработчик, предназначенный для использования в Unix/Linux, следит, не изменился ли файл с момента последнего вызова emit. (Считается, что файл изменился, если изменились его устройство или inode.) Если файл изменился, старый поток файла закрывается, а файл открывается для получения нового потока.15431544Этот обработчик не подходит для использования в Windows, поскольку в Windows открытые файлы журналов нельзя перемещать или переименовывать – система логирования открывает файлы с эксклюзивными блокировками, – поэтому в таком обработчике нет необходимости. Кроме того, *ST\_INO* не поддерживается в Windows; `stat()` всегда возвращает ноль для этого значения.15451546#### `class logging.handlers.WatchedFileHandler(filename[, mode[, encoding[, delay]]])`15471548Возвращает новый экземпляр класса [`WatchedFileHandler`](https://python-all.ru/3.1/library/logging.html#logging.handlers.WatchedFileHandler). Указанный файл открывается и используется как поток для логирования. Если *mode* не указан, используется `'a'`. Если *encoding* не равен *None*, файл открывается с указанной кодировкой. Если *delay* равен true, открытие файла откладывается до первого вызова [`emit()`](https://python-all.ru/3.1/library/logging.html#logging.handlers.WatchedFileHandler.emit). По умолчанию файл растёт бесконечно.15491550#### `emit(record)`15511552Выводит запись в файл, но сначала проверяет, изменился ли файл. Если изменился, существующий поток сбрасывается и закрывается, а файл открывается заново, после чего запись выводится в файл.15531554### 15.6.14.5. RotatingFileHandler15551556Класс [`RotatingFileHandler`](https://python-all.ru/3.1/library/logging.html#logging.handlers.RotatingFileHandler), находящийся в модуле `logging.handlers`, поддерживает ротацию файлов журналов на диске.15571558#### `class logging.handlers.RotatingFileHandler(filename, mode='a', maxBytes=0, backupCount=0, encoding=None, delay=0)`15591560Возвращает новый экземпляр класса [`RotatingFileHandler`](https://python-all.ru/3.1/library/logging.html#logging.handlers.RotatingFileHandler). Указанный файл открывается и используется как поток для журналирования. Если *mode* не указан, используется `'a'`. Если *encoding* не равен *None*, файл открывается с указанной кодировкой. Если *delay* равен истине, открытие файла откладывается до первого вызова [`emit()`](https://python-all.ru/3.1/library/logging.html#logging.handlers.RotatingFileHandler.emit). По умолчанию файл растёт бесконечно.15611562С помощью параметров *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` и так далее.15631564#### `doRollover()`15651566Выполняет ротацию, как описано выше.15671568#### `emit(record)`15691570Записывает запись (record) в файл, выполняя при необходимости ротацию, как описано ранее.15711572### 15.6.14.6. TimedRotatingFileHandler15731574Класс [`TimedRotatingFileHandler`](https://python-all.ru/3.1/library/logging.html#logging.handlers.TimedRotatingFileHandler), находящийся в модуле `logging.handlers`, поддерживает ротацию файлов журналов на диске через заданные промежутки времени.15751576#### `class logging.handlers.TimedRotatingFileHandler(filename, when='h', interval=1, backupCount=0, encoding=None, delay=0, utc=False)`15771578Возвращает новый экземпляр класса [`TimedRotatingFileHandler`](https://python-all.ru/3.1/library/logging.html#logging.handlers.TimedRotatingFileHandler). Указанный файл открывается и используется как поток для журналирования. При ротации также устанавливается суффикс имени файла. Ротация происходит на основе произведения *when* и *interval*.15791580Параметр *when* указывает тип *interval*. Список возможных значений приведён ниже. Обратите внимание, что регистр не учитывается.15811582| Значение | Тип интервала |1583| --- | --- |1584| `'S'` | Секунды |1585| `'M'` | Минуты |1586| `'H'` | Часы |1587| `'D'` | Дни |1588| `'W'` | День недели (0 = понедельник) |1589| `'midnight'` | Переключение в полночь |15901591Система будет сохранять старые файлы журнала, добавляя расширения к имени файла. Расширения основаны на дате и времени, с использованием формата strftime `%Y-%m-%d_%H-%M-%S` или его начальной части, в зависимости от интервала ротации.15921593При первом вычислении времени следующей ротации (при создании обработчика) используется время последнего изменения существующего файла журнала или, в противном случае, текущее время, чтобы определить, когда произойдёт следующая ротация.15941595Если аргумент *utc* равен true, используется время в UTC; в противном случае используется местное время.15961597Если *backupCount* не равен нулю, сохраняется не более *backupCount* файлов; если при ротации должно быть создано больше файлов, самый старый удаляется. Логика удаления использует интервал для определения того, какие файлы нужно удалить, поэтому изменение интервала может привести к тому, что старые файлы останутся.15981599Если *delay* равен true, открытие файла откладывается до первого вызова [`emit()`](https://python-all.ru/3.1/library/logging.html#logging.handlers.TimedRotatingFileHandler.emit).16001601#### `doRollover()`16021603Выполняет ротацию, как описано выше.16041605#### `emit(record)`16061607Выводит запись в файл, обеспечивая ротацию, как описано выше.16081609### 15.6.14.7. SocketHandler16101611Класс [`SocketHandler`](https://python-all.ru/3.1/library/logging.html#logging.handlers.SocketHandler), расположенный в модуле `logging.handlers`, отправляет вывод журнала на сетевой сокет. Базовый класс использует TCP-сокет.16121613#### `class logging.handlers.SocketHandler(host, port)`16141615Возвращает новый экземпляр класса [`SocketHandler`](https://python-all.ru/3.1/library/logging.html#logging.handlers.SocketHandler), предназначенного для взаимодействия с удаленной машиной, адрес которой задается *host* и *port*.16161617#### `close()`16181619Закрывает сокет.16201621#### `emit()`16221623Сериализует словарь атрибутов записи и записывает его в сокет в бинарном формате. Если при работе с сокетом возникает ошибка, пакет молча отбрасывается. Если соединение было ранее потеряно, оно восстанавливается. Для десериализации записи на принимающей стороне в16241625`LogRecord`16261627используйте функцию16281629`makeLogRecord()`16301631.16321633#### `handleError()`16341635Обрабатывает ошибку, возникшую во время16361637[`emit()`](https://python-all.ru/3.1/library/logging.html#logging.handlers.SocketHandler.emit)16381639. Наиболее вероятная причина – потеря соединения. Закрывает сокет, чтобы можно было повторить попытку при следующем событии.16401641#### `makeSocket()`16421643Это фабричный метод, позволяющий подклассам определять точный тип сокета, который они хотят. Реализация по умолчанию создает TCP-сокет (16441645[`socket.SOCK_STREAM`](https://python-all.ru/3.1/library/socket.html#socket.SOCK_STREAM)16461647).16481649#### `makePickle(record)`16501651Сериализует словарь атрибутов записи в двоичном формате с префиксом длины и возвращает его готовым к передаче через сокет.16521653Обратите внимание, что модуль pickle не является полностью безопасным. Если вас беспокоит безопасность, вы можете переопределить этот метод, чтобы реализовать более надёжный механизм. Например, можно подписывать данные с помощью HMAC и проверять подпись на принимающей стороне, либо отключить десериализацию глобальных объектов на принимающей стороне.16541655#### `send(packet)`16561657Отправляет сериализованную с помощью pickle строку16581659*packet*16601661в сокет. Эта функция допускает частичную отправку, которая может произойти, когда сеть занята.16621663### 15.6.14.8. DatagramHandler16641665Класс [`DatagramHandler`](https://python-all.ru/3.1/library/logging.html#logging.handlers.DatagramHandler), расположенный в модуле `logging.handlers` , наследуется от [`SocketHandler`](https://python-all.ru/3.1/library/logging.html#logging.handlers.SocketHandler) для поддержки отправки сообщений журнала через UDP-сокеты.16661667#### `class logging.handlers.DatagramHandler(host, port)`16681669Возвращает новый экземпляр класса [`DatagramHandler`](https://python-all.ru/3.1/library/logging.html#logging.handlers.DatagramHandler), предназначенного для взаимодействия с удаленной машиной, адрес которой задается *host* и *port*.16701671#### `emit()`16721673Сериализует словарь атрибутов записи и записывает его в сокет в бинарном формате. Если при работе с сокетом возникает ошибка, пакет молча отбрасывается. Для десериализации записи на принимающей стороне в16741675`LogRecord`16761677используйте функцию16781679`makeLogRecord()`16801681.16821683#### `makeSocket()`16841685Фабричный метод16861687[`SocketHandler`](https://python-all.ru/3.1/library/logging.html#logging.handlers.SocketHandler)16881689здесь переопределен для создания UDP-сокета (16901691[`socket.SOCK_DGRAM`](https://python-all.ru/3.1/library/socket.html#socket.SOCK_DGRAM)16921693).16941695#### `send(s)`16961697Отправляет сериализованную с помощью pickle строку в сокет.16981699### 15.6.14.9. SysLogHandler17001701Класс [`SysLogHandler`](https://python-all.ru/3.1/library/logging.html#logging.handlers.SysLogHandler), расположенный в модуле `logging.handlers`, поддерживает отправку сообщений журнала в удаленный или локальный системный журнал Unix.17021703#### `class logging.handlers.SysLogHandler(address=('localhost', SYSLOG_UDP_PORT), facility=LOG_USER)`17041705Возвращает новый экземпляр класса [`SysLogHandler`](https://python-all.ru/3.1/library/logging.html#logging.handlers.SysLogHandler), предназначенный для связи с удаленной Unix-машиной, адрес которой задается параметром *address* в виде кортежа `(host, port)`. Если *address* не указан, используется `('localhost', 514)`. Адрес используется для открытия UDP-сокета. Альтернативой кортежу `(host, port)` является указание адреса в виде строки, например “/dev/log”. В этом случае для отправки сообщения в syslog используется сокет домена Unix. Если *facility* не указан, используется `LOG_USER`.17061707#### `close()`17081709Закрывает сокет, подключённый к удалённому узлу.17101711#### `emit(record)`17121713Запись форматируется и отправляется на сервер syslog. Если присутствует информация об исключении, она17141715*не*17161717отправляется на сервер.17181719#### `encodePriority(facility, priority)`17201721Кодирует средство и приоритет в целое число. Можно передавать строки или целые числа – если передаются строки, внутренние словари сопоставлений используются для преобразования их в целые числа.17221723Символические значения `LOG_` определены в [`SysLogHandler`](https://python-all.ru/3.1/library/logging.html#logging.handlers.SysLogHandler) и соответствуют значениям, определённым в заголовочном файле `sys/syslog.h`.17241725**Приоритеты**17261727| Имя (строка) | Символическое значение |1728| --- | --- |1729| `alert` | LOG\_ALERT |1730| `crit` или `critical` | LOG\_CRIT |1731| `отладка` | LOG\_DEBUG |1732| `аварийная` или `паника` | LOG\_EMERG |1733| `ошибка` или `ошибка` | LOG\_ERR |1734| `информация` | LOG\_INFO |1735| `уведомление` | LOG\_NOTICE |1736| `предупреждение` или `предупреждение` | LOG\_WARNING |17371738**Средства**17391740| Имя (строка) | Символическое значение |1741| --- | --- |1742| `аутентификация` | LOG\_AUTH |1743| `authpriv` | LOG\_AUTHPRIV |1744| `cron` | LOG\_CRON |1745| `демон` | LOG\_DAEMON |1746| `ftp` | LOG\_FTP |1747| `ядро` | LOG\_KERN |1748| `lpr` | LOG\_LPR |1749| `почта` | LOG\_MAIL |1750| `новости` | LOG\_NEWS |1751| `syslog` | LOG\_SYSLOG |1752| `user` | LOG\_USER |1753| `uucp` | LOG\_UUCP |1754| `local0` | LOG\_LOCAL0 |1755| `local1` | LOG\_LOCAL1 |1756| `local2` | LOG\_LOCAL2 |1757| `local3` | LOG\_LOCAL3 |1758| `local4` | LOG\_LOCAL4 |1759| `local5` | LOG\_LOCAL5 |1760| `local6` | LOG\_LOCAL6 |1761| `local7` | LOG\_LOCAL7 |17621763#### `mapPriority(levelname)`17641765Сопоставляет имя уровня журналирования с именем приоритета syslog. Возможно, потребуется переопределить его, если используются пользовательские уровни или стандартный алгоритм не подходит для ваших нужд. Стандартный алгоритм сопоставляет17661767`DEBUG`17681769,17701771`INFO`17721773,17741775`WARNING`17761777,17781779`ERROR`17801781и17821783`CRITICAL`17841785с соответствующими именами syslog, а все остальные имена уровней – с «warning».17861787### 15.6.14.10. NTEventLogHandler17881789Класс [`NTEventLogHandler`](https://python-all.ru/3.1/library/logging.html#logging.handlers.NTEventLogHandler), находящийся в модуле `logging.handlers`, поддерживает отправку сообщений журналирования в журнал событий локальной Windows NT, Windows 2000 или Windows XP. Перед использованием необходимо установить расширения Win32 от Марка Хэммонда для Python.17901791#### `class logging.handlers.NTEventLogHandler(appname, dllname=None, logtype='Application')`17921793Возвращает новый экземпляр класса [`NTEventLogHandler`](https://python-all.ru/3.1/library/logging.html#logging.handlers.NTEventLogHandler). Параметр *appname* используется для определения имени приложения, отображаемого в журнале событий. С использованием этого имени создается соответствующая запись реестра. Параметр *dllname* должен содержать полный путь к .dll или .exe-файлу с определениями сообщений для хранения в журнале (если не указан, используется `'win32service.pyd'` – он устанавливается вместе с расширениями Win32 и содержит несколько базовых определений сообщений-заполнителей. Обратите внимание, что использование этих заполнителей приведет к увеличению размера журнала, так как весь источник сообщений хранится в журнале. Если нужны более компактные журналы, следует передать имя собственного .dll или .exe, содержащего нужные определения сообщений). Параметр *logtype* может быть одним из `'Application'`, `'System'` или `'Security'`, по умолчанию `'Application'`.17941795#### `close()`17961797На этом этапе можно удалить имя приложения из реестра как источник записей журнала событий. Однако после этого события не будут отображаться в средстве просмотра журнала событий так, как предполагалось – для получения имени .dll требуется доступ к реестру. Текущая версия этого не делает.17981799#### `emit(record)`18001801Определяет идентификатор сообщения, категорию и тип события, а затем записывает сообщение в журнал событий NT.18021803#### `getEventCategory(record)`18041805Возвращает категорию события для записи. Переопределите этот метод, чтобы указать собственные категории. Данная версия возвращает 0.18061807#### `getEventType(record)`18081809Возвращает тип события для записи. Переопределите этот метод, если нужно задать собственные типы. В данной версии используется сопоставление через атрибут typemap обработчика, который устанавливается в18101811[`__init__()`](https://python-all.ru/3.1/reference/datamodel.html#object.__init__)18121813в виде словаря, содержащего сопоставления для18141815`DEBUG`18161817,18181819`INFO`18201821,18221823`WARNING`18241825,18261827`ERROR`18281829и18301831`CRITICAL`18321833. При использовании собственных уровней потребуется либо переопределить этот метод, либо поместить подходящий словарь в атрибут18341835*typemap*18361837обработчика.18381839#### `getMessageID(record)`18401841Возвращает идентификатор сообщения для записи. При использовании собственных сообщений можно передать в логгер идентификатор18421843*msg*18441845вместо строки форматирования. Затем здесь можно выполнить поиск по словарю для получения идентификатора сообщения. Данная версия возвращает 1 – базовый идентификатор сообщения в18461847`win32service.pyd`18481849.18501851### 15.6.14.11. SMTPHandler18521853Класс [`SMTPHandler`](https://python-all.ru/3.1/library/logging.html#logging.handlers.SMTPHandler), находящийся в модуле `logging.handlers`, поддерживает отправку сообщений журналирования на адрес электронной почты через SMTP.18541855#### `class logging.handlers.SMTPHandler(mailhost, fromaddr, toaddrs, subject, credentials=None)`18561857Возвращает новый экземпляр класса [`SMTPHandler`](https://python-all.ru/3.1/library/logging.html#logging.handlers.SMTPHandler). Экземпляр инициализируется адресами отправителя, получателя и темой письма. Параметр *toaddrs* должен быть списком строк. Для указания нестандартного порта SMTP используется кортеж (host, port) в аргументе *mailhost*. Если передана строка, используется стандартный порт SMTP. Если SMTP-сервер требует аутентификации, можно указать кортеж (username, password) в аргументе *credentials*.18581859#### `emit(record)`18601861Форматирует запись и отправляет её указанным получателям.18621863#### `getSubject(record)`18641865Чтобы задать тему письма, зависящую от записи, переопределите этот метод.18661867### 15.6.14.12. MemoryHandler18681869Класс [`MemoryHandler`](https://python-all.ru/3.1/library/logging.html#logging.handlers.MemoryHandler), находящийся в модуле `logging.handlers`, поддерживает буферизацию записей журналирования в памяти с периодической отправкой их в обработчик *target*. Сброс происходит, когда буфер полон или когда появляется событие определённой важности или выше.18701871[`MemoryHandler`](https://python-all.ru/3.1/library/logging.html#logging.handlers.MemoryHandler) является подклассом более общего [`BufferingHandler`](https://python-all.ru/3.1/library/logging.html#logging.handlers.BufferingHandler), который является абстрактным классом. Он буферизирует записи журналирования в памяти. Каждый раз, когда запись добавляется в буфер, вызывается `shouldFlush()` для проверки необходимости сброса буфера. Если сброс нужен, то `flush()` должен выполнить необходимые действия.18721873#### `class logging.handlers.BufferingHandler(capacity)`18741875Инициализирует обработчик буфером указанной ёмкости.18761877#### `emit(record)`18781879Добавляет запись в буфер. Если18801881[`shouldFlush()`](https://python-all.ru/3.1/library/logging.html#logging.handlers.BufferingHandler.shouldFlush)18821883возвращает true, вызывает18841885[`flush()`](https://python-all.ru/3.1/library/logging.html#logging.handlers.BufferingHandler.flush)18861887для обработки буфера.18881889#### `flush()`18901891Вы можете переопределить этот метод, чтобы задать собственное поведение при сбросе. Текущая версия просто очищает буфер.18921893#### `shouldFlush(record)`18941895Возвращает true, если буфер заполнен до ёмкости. Этот метод можно переопределить для реализации собственных стратегий сброса.18961897#### `class logging.handlers.MemoryHandler(capacity, flushLevel=ERROR, target=None)`18981899Возвращает новый экземпляр класса [`MemoryHandler`](https://python-all.ru/3.1/library/logging.html#logging.handlers.MemoryHandler). Экземпляр инициализируется с размером буфера *capacity*. Если *flushLevel* не указан, используется `ERROR`. Если *target* не указан, перед тем как обработчик начнёт работать, цель нужно задать с помощью [`setTarget()`](https://python-all.ru/3.1/library/logging.html#logging.handlers.MemoryHandler.setTarget).19001901#### `close()`19021903Вызывает19041905[`flush()`](https://python-all.ru/3.1/library/logging.html#logging.handlers.MemoryHandler.flush)19061907, устанавливает цель в19081909[`None`](https://python-all.ru/3.1/library/constants.html#None)19101911и очищает буфер.19121913#### `flush()`19141915Для19161917[`MemoryHandler`](https://python-all.ru/3.1/library/logging.html#logging.handlers.MemoryHandler)19181919сброс означает просто отправку буферизированных записей целевому обработчику, если он есть. Переопределите, если требуется иное поведение.19201921#### `setTarget(target)`19221923Устанавливает целевой обработчик для данного обработчика.19241925#### `shouldFlush(record)`19261927Проверяет, заполнен ли буфер или поступила запись уровня19281929*flushLevel*19301931или выше.19321933### 15.6.14.13. HTTPHandler19341935Класс [`HTTPHandler`](https://python-all.ru/3.1/library/logging.html#logging.handlers.HTTPHandler), расположенный в модуле `logging.handlers`, поддерживает отправку сообщений журнала на веб-сервер, используя семантику `GET` или `POST`.19361937#### `class logging.handlers.HTTPHandler(host, url, method='GET')`19381939Возвращает новый экземпляр класса [`HTTPHandler`](https://python-all.ru/3.1/library/logging.html#logging.handlers.HTTPHandler). Экземпляр инициализируется адресом хоста, URL и HTTP-методом. *host* может быть в формате `host:port`, если требуется указать определённый номер порта. Если *method* не указан, используется `GET`.19401941#### `emit(record)`19421943Отправляет запись на веб-сервер в виде словаря в процентной кодировке.19441945## 15.6.15. Объекты Formatter19461947[`Formatter`](https://python-all.ru/3.1/library/logging.html#logging.Formatter) имеют следующие атрибуты и методы. Они отвечают за преобразование [`LogRecord`](https://python-all.ru/3.1/library/logging.html#logging.LogRecord) в (обычно) строку, которая может интерпретироваться человеком или внешней системой. Базовый [`Formatter`](https://python-all.ru/3.1/library/logging.html#logging.Formatter) позволяет задать строку форматирования. Если она не указана, используется значение по умолчанию `'%(message)s'`.19481949Объект Formatter можно инициализировать строкой форматирования, использующей атрибуты [`LogRecord`](https://python-all.ru/3.1/library/logging.html#logging.LogRecord), например, как в упомянутом выше значении по умолчанию, использующем тот факт, что сообщение пользователя и аргументы предварительно форматируются в атрибут [`LogRecord`](https://python-all.ru/3.1/library/logging.html#logging.LogRecord)'s *message*. Эта строка форматирования содержит стандартные %-ключи сопоставления Python. Дополнительную информацию о форматировании строк см. в разделе [*Old String Formatting Operations*](https://python-all.ru/3.1/library/stdtypes.html#old-string-formatting).19501951Полезные ключи отображения в [`LogRecord`](https://python-all.ru/3.1/library/logging.html#logging.LogRecord) на данный момент:19521953| Формат | Описание |1954| --- | --- |1955| `%(name)s` | Имя логгера (канала журналирования). |1956| `%(levelno)s` | Числовой уровень логирования сообщения (`DEBUG`, `INFO`, `WARNING`, `ERROR`, `CRITICAL`). |1957| `%(levelname)s` | Текстовый уровень логирования сообщения (`'DEBUG'`, `'INFO'`, `'WARNING'`, `'ERROR'`, `'CRITICAL'`). |1958| `%(pathname)s` | Полный путь к исходному файлу, из которого был сделан вызов логирования (если доступен). |1959| `%(filename)s` | Имя файла из пути. |1960| `%(module)s` | Модуль (часть имени файла). |1961| `%(funcName)s` | Имя функции, содержащей вызов логирования. |1962| `%(lineno)d` | Номер строки исходного кода, из которой был сделан вызов логирования (если доступен). |1963| `%(created)f` | Время создания [`LogRecord`](https://python-all.ru/3.1/library/logging.html#logging.LogRecord) (возвращаемое [`time.time()`](https://python-all.ru/3.1/library/time.html#time.time)). |1964| `%(relativeCreated)d` | Время в миллисекундах, когда LogRecord был создан, относительно времени загрузки модуля logging. |1965| `%(asctime)s` | Человекочитаемое время создания [`LogRecord`](https://python-all.ru/3.1/library/logging.html#logging.LogRecord). По умолчанию оно имеет вид “2003-07-08 16:49:45,896” (числа после запятой – миллисекунды времени). |1966| `%(msecs)d` | Миллисекундная часть времени, когда был создан [`LogRecord`](https://python-all.ru/3.1/library/logging.html#logging.LogRecord). |1967| `%(поток)d` | Идентификатор потока (если доступен). |1968| `%(threadName)s` | Имя потока (если доступно). |1969| `%(процесс)d` | Идентификатор процесса (если доступен). |1970| `%(processName)s` | Имя процесса (если доступно). |1971| `%(message)s` | Записанное сообщение, вычисляемое как `msg % args`. |19721973#### `class logging.Formatter(fmt=None, datefmt=None)`19741975Возвращает новый экземпляр класса [`Formatter`](https://python-all.ru/3.1/library/logging.html#logging.Formatter). Экземпляр инициализируется строкой форматирования для всего сообщения, а также строкой форматирования для части даты/времени сообщения. Если *fmt* не указан, используется `'%(message)s'`. Если *datefmt* не указан, используется формат даты ISO8601.19761977#### `format(record)`19781979Словарь атрибутов записи используется как операнд для операции форматирования строки. Возвращает результирующую строку. Перед форматированием словаря выполняется несколько подготовительных шагов. Атрибут19801981*message*19821983записи вычисляется с помощью19841985*msg*19861987%19881989*args*19901991. Если строка форматирования содержит19921993`'(asctime)'`19941995, вызывается19961997[`formatTime()`](https://python-all.ru/3.1/library/logging.html#logging.Formatter.formatTime)19981999для форматирования времени события. Если присутствует информация об исключении, она форматируется с помощью20002001[`formatException()`](https://python-all.ru/3.1/library/logging.html#logging.Formatter.formatException)20022003и добавляется к сообщению. Обратите внимание, что отформатированная информация об исключении кэшируется в атрибуте20042005*exc\_text*20062007. Это полезно, поскольку информацию об исключении можно сериализовать и отправить по сети, но следует быть осторожным, если имеется более одного подкласса20082009[`Formatter`](https://python-all.ru/3.1/library/logging.html#logging.Formatter)20102011, который настраивает форматирование информации об исключении. В этом случае вам придется очищать кэшированное значение после того, как форматировщик выполнит свое форматирование, чтобы следующий форматировщик, обрабатывающий событие, не использовал кэшированное значение, а пересчитал его заново.20122013#### `formatTime(record, datefmt=None)`20142015Этот метод должен вызываться из20162017[`format()`](https://python-all.ru/3.1/library/functions.html#format)20182019форматировщиком, который хочет использовать отформатированное время. Этот метод может быть переопределен в форматировщиках для любых специфических требований, но базовое поведение следующее: если указан20202021*datefmt*20222023(строка), он используется с20242025[`time.strftime()`](https://python-all.ru/3.1/library/time.html#time.strftime)20262027для форматирования времени создания записи. В противном случае используется формат ISO8601. Возвращается результирующая строка.20282029#### `formatException(exc_info)`20302031Форматирует указанную информацию об исключении (стандартный кортеж исключения, возвращаемый20322033[`sys.exc_info()`](https://python-all.ru/3.1/library/sys.html#sys.exc_info)20342035) в виде строки. Эта реализация по умолчанию просто использует20362037[`traceback.print_exception()`](https://python-all.ru/3.1/library/traceback.html#traceback.print_exception)20382039. Полученная строка возвращается.20402041## 15.6.16. Объекты Filter20422043[`Filter`](https://python-all.ru/3.1/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” и т.д. Если инициализирован пустой строкой, пропускаются все события.20442045#### `class logging.Filter(name='')`20462047Возвращает экземпляр класса [`Filter`](https://python-all.ru/3.1/library/logging.html#logging.Filter). Если указан *name*, он задаёт имя логгера, который вместе со своими дочерними элементами будет пропускать события через фильтр. Если *name* – пустая строка, пропускаются все события.20482049#### `filter(record)`20502051Следует ли регистрировать указанную запись? Возвращает ноль, если нет, и ненулевое значение, если да. Если это уместно, запись может быть изменена на месте данным методом.20522053Обратите внимание, что фильтры, прикреплённые к обработчикам, проверяются всякий раз, когда обработчик порождает событие, тогда как фильтры, прикреплённые к регистраторам, проверяются всякий раз, когда событие регистрируется в обработчике (с помощью [`debug()`](https://python-all.ru/3.1/library/logging.html#logging.debug), [`info()`](https://python-all.ru/3.1/library/logging.html#logging.info) и т.д.). Это означает, что события, порождённые дочерними регистраторами, не будут отфильтрованы настройкой фильтра регистратора, если только фильтр не был также применён к этим дочерним регистраторам.20542055## 15.6.17. Объекты LogRecord20562057Экземпляры [`LogRecord`](https://python-all.ru/3.1/library/logging.html#logging.LogRecord) создаются автоматически регистратором `Logger` каждый раз при регистрации чего-либо, и могут быть созданы вручную с помощью [`makeLogRecord()`](https://python-all.ru/3.1/library/logging.html#logging.makeLogRecord) (например, из упакованного события, полученного по сети).20582059#### `class logging.LogRecord(name, lvl, pathname, lineno, msg, args, exc_info, func=None)`20602061Содержит всю информацию, относящуюся к регистрируемому событию.20622063Основная информация передаётся в параметрах [`msg`](https://python-all.ru/3.1/library/logging.html#logging.LogRecord.msg) и [`args`](https://python-all.ru/3.1/library/logging.html#logging.LogRecord.args), которые объединяются с помощью `msg % args` для создания поля [`message`](https://python-all.ru/3.1/library/logging.html#logging.LogRecord.message) записи.20642065#### `args`20662067Кортеж аргументов, используемых при форматировании20682069[`msg`](https://python-all.ru/3.1/library/logging.html#logging.LogRecord.msg)20702071.20722073#### `exc_info`20742075Кортеж исключения (наподобие20762077*sys.exc\_info*20782079) или20802081*None*20822083, если информация об исключении недоступна.20842085#### `func`20862087Имя исходной функции (то есть в которой был выполнен вызов регистрации).20882089#### `lineno`20902091Номер строки в исходном файле.20922093#### `lvl`20942095Числовой уровень регистрации.20962097#### `message`20982099Связывается с результатом21002101[`getMessage()`](https://python-all.ru/3.1/library/logging.html#logging.LogRecord.getMessage)21022103при вызове21042105[`Formatter.format(record)`](https://python-all.ru/3.1/library/logging.html#logging.Formatter.format)21062107.21082109#### `msg`21102111Предоставленная пользователем21122113[*строка форматирования*](https://python-all.ru/3.1/library/string.html#string-formatting)21142115или произвольный объект (см.21162117[*Использование произвольных объектов в качестве сообщений*](https://python-all.ru/3.1/library/logging.html#arbitrary-object-messages)21182119), используемый в21202121[`getMessage()`](https://python-all.ru/3.1/library/logging.html#logging.LogRecord.getMessage)21222123.21242125#### `name`21262127Имя регистратора, который выдал запись.21282129#### `pathname`21302131Абсолютный путь к исходному файлу.21322133#### `getMessage()`21342135Возвращает сообщение для данного экземпляра21362137[`LogRecord`](https://python-all.ru/3.1/library/logging.html#logging.LogRecord)21382139после объединения любых предоставленных пользователем аргументов с сообщением.21402141## 15.6.18. Объекты LoggerAdapter21422143Экземпляры [`LoggerAdapter`](https://python-all.ru/3.1/library/logging.html#logging.LoggerAdapter) используются для удобной передачи контекстной информации в вызовы журналирования. Пример использования см. в разделе [*добавление контекстной информации в вывод журнала*](https://python-all.ru/3.1/library/logging.html#context-info).21442145#### `class logging.LoggerAdapter(logger, extra)`21462147Возвращает экземпляр [`LoggerAdapter`](https://python-all.ru/3.1/library/logging.html#logging.LoggerAdapter), инициализированный базовым экземпляром `Logger` и объектом, подобным словарю.21482149#### `process(msg, kwargs)`21502151Изменяет сообщение и/или именованные аргументы, переданные в вызов логирования, чтобы вставить контекстную информацию. Эта реализация берёт объект, переданный как21522153*extra*21542155конструктору, и добавляет его в21562157*kwargs*21582159с ключом 'extra'. Возвращаемое значение – кортеж (21602161*msg*21622163,21642165*kwargs*21662167), содержащий (возможно изменённые) версии переданных аргументов.21682169Помимо перечисленного, [`LoggerAdapter`](https://python-all.ru/3.1/library/logging.html#logging.LoggerAdapter) поддерживает все методы логирования класса `Logger`, то есть [`debug()`](https://python-all.ru/3.1/library/logging.html#logging.debug), [`info()`](https://python-all.ru/3.1/library/logging.html#logging.info), [`warning()`](https://python-all.ru/3.1/library/logging.html#logging.warning), [`error()`](https://python-all.ru/3.1/library/logging.html#logging.error), [`exception()`](https://python-all.ru/3.1/library/logging.html#logging.exception), [`critical()`](https://python-all.ru/3.1/library/logging.html#logging.critical) и [`log()`](https://python-all.ru/3.1/library/logging.html#logging.log). Эти методы имеют те же сигнатуры, что и их аналоги в `Logger`, поэтому экземпляры обоих типов можно использовать взаимозаменямо.21702171> Метод2172>2173> `isEnabledFor()`2174>2175> был добавлен в2176>2177> [`LoggerAdapter`](https://python-all.ru/3.1/library/logging.html#logging.LoggerAdapter)2178>2179> . Этот метод делегирует вызов базовому логгеру.21802181## 15.6.19. Потокобезопасность21822183Модуль logging спроектирован так, чтобы быть потокобезопасным без каких-либо специальных действий требуемых от его клиентов. Это достигается за счёт использования threading блокировок; одна блокировка сериализует доступ к общим данным модуля, а каждый обработчик также создаёт блокировку для сериализации доступа к своему нижележащему вводу-выводу.21842185Если вы реализуете асинхронные обработчики сигналов с помощью модуля [`signal`](https://python-all.ru/3.1/library/signal.html#module-signal), вы можете не иметь возможности использовать логирование из таких обработчиков. Это связано с тем, что реализации блокировок в модуле [`threading`](https://python-all.ru/3.1/library/threading.html#module-threading) не всегда реентерабельны, и поэтому их нельзя вызывать из таких обработчиков сигналов.21862187## 15.6.20. Конфигурация21882189### 15.6.20.1. Функции конфигурации21902191Следующие функции настраивают модуль логирования. Они находятся в модуле `logging.config`. Их использование необязательно – настроить модуль логирования можно как с помощью этих функций, так и напрямую через основной API (определённый в самом `logging`) и определением обработчиков, объявленных либо в `logging`, либо в `logging.handlers`.21922193#### `logging.fileConfig(fname, defaults=None, disable_existing_loggers=True)`21942195Читает конфигурацию логирования из файла формата [`configparser`](https://python-all.ru/3.1/library/configparser.html#module-configparser) с именем *fname*. Эту функцию можно вызывать несколько раз из приложения, что позволяет конечному пользователю выбирать из различных готовых конфигураций (если разработчик предоставляет механизм для отображения вариантов и загрузки выбранной конфигурации). Значения по умолчанию, передаваемые в ConfigParser, можно указать в аргументе *defaults*.21962197Если *disable\_existing\_loggers* равен True, все существующие логгеры, которые не являются дочерними по отношению к именованным логгерам, будут отключены.21982199#### `logging.listen(port=DEFAULT_LOGGING_CONFIG_PORT)`22002201Запускает сокет-сервер на указанном порту и ожидает новые конфигурации. Если порт не указан, используется значение по умолчанию модуля `DEFAULT_LOGGING_CONFIG_PORT`. Конфигурации логирования отправляются в виде файла, пригодного для обработки функцией [`fileConfig()`](https://python-all.ru/3.1/library/logging.html#logging.fileConfig). Возвращает экземпляр `поток`, на котором можно вызвать `start()` для запуска сервера и к которому при необходимости можно применить `join()`. Чтобы остановить сервер, вызовите [`stopListening()`](https://python-all.ru/3.1/library/logging.html#logging.stopListening).22022203Чтобы отправить конфигурацию в сокет, прочитайте файл конфигурации и отправьте его в сокет в виде строки байтов, перед которой идёт четырёхбайтовая длина, упакованная в двоичном виде с помощью `struct.pack('>L', n)`.22042205#### `logging.stopListening()`22062207Останавливает сервер прослушивания, созданный вызовом22082209[`listen()`](https://python-all.ru/3.1/library/logging.html#logging.listen)22102211. Обычно вызывается перед вызовом22122213`join()`22142215на возвращаемом значении22162217[`listen()`](https://python-all.ru/3.1/library/logging.html#logging.listen)22182219.22202221### 15.6.20.2. Формат файла конфигурации22222223The configuration file format understood by [`fileConfig()`](https://python-all.ru/3.1/library/logging.html#logging.fileConfig) is based on [`configparser`](https://python-all.ru/3.1/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]`.22242225Примеры таких разделов в файле приведены ниже.22262227```python2228[loggers]2229keys=root,log02,log03,log04,log05,log06,log0722302231[handlers]2232keys=hand01,hand02,hand03,hand04,hand05,hand06,hand07,hand08,hand0922332234[formatters]2235keys=form01,form02,form03,form04,form05,form06,form07,form08,form092236```22372238Корневой регистратор должен указывать уровень и список обработчиков. Пример раздела корневого регистратора приведен ниже.22392240```python2241[logger_root]2242level=NOTSET2243handlers=hand012244```22452246The `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.1/library/functions.html#eval)uated in the context of the `logging` package’s namespace.22472248The `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.22492250Для регистраторов, отличных от корневого, требуется некоторая дополнительная информация. Это проиллюстрировано следующим примером.22512252```python2253[logger_parser]2254level=DEBUG2255handlers=hand012256propagate=12257qualname=compiler.parser2258```22592260The `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.22612262Разделы, которые задают конфигурацию обработчика, иллюстрируются следующим примером.22632264```python2265[handler_hand01]2266class=StreamHandler2267level=NOTSET2268formatter=form012269args=(sys.stdout,)2270```22712272Запись `class` указывает класс обработчика (определяемый с помощью [`eval()`](https://python-all.ru/3.1/library/functions.html#eval) в пространстве имён пакета `logging`). Значение `level` интерпретируется так же, как для логгеров, а `NOTSET` означает «логировать всё».22732274The `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.22752276Запись `args` при вычислении [`eval()`](https://python-all.ru/3.1/library/functions.html#eval) в контексте пространства имён пакета `logging` представляет собой список аргументов для конструктора класса обработчика. Обратитесь к конструкторам соответствующих обработчиков или к примерам ниже, чтобы узнать, как формируются типичные записи.22772278```python2279[handler_hand02]2280class=FileHandler2281level=DEBUG2282formatter=form022283args=('python.log', 'w')22842285[handler_hand03]2286class=handlers.SocketHandler2287level=INFO2288formatter=form032289args=('localhost', handlers.DEFAULT_TCP_LOGGING_PORT)22902291[handler_hand04]2292class=handlers.DatagramHandler2293level=WARN2294formatter=form042295args=('localhost', handlers.DEFAULT_UDP_LOGGING_PORT)22962297[handler_hand05]2298class=handlers.SysLogHandler2299level=ERROR2300formatter=form052301args=(('localhost', handlers.SYSLOG_UDP_PORT), handlers.SysLogHandler.LOG_USER)23022303[handler_hand06]2304class=handlers.NTEventLogHandler2305level=CRITICAL2306formatter=form062307args=('Python Application', '', 'Application')23082309[handler_hand07]2310class=handlers.SMTPHandler2311level=WARN2312formatter=form072313args=('localhost', 'from@abc', ['user1@abc', 'user2@xyz'], 'Logger Subject')23142315[handler_hand08]2316class=handlers.MemoryHandler2317level=NOTSET2318formatter=form082319target=2320args=(10, ERROR)23212322[handler_hand09]2323class=handlers.HTTPHandler2324level=NOTSET2325formatter=form092326args=('localhost:9022', '/log', 'GET')2327```23282329Разделы, которые задают конфигурацию форматировщика, обычно выглядят следующим образом.23302331```python2332[formatter_form01]2333format=F1 %(asctime)s %(levelname)s %(message)s2334datefmt=2335class=logging.Formatter2336```23372338Запись `format` – это общая строка формата, а запись `datefmt` – это строка формата даты/времени, совместимая с `strftime()`. Если она пуста, пакет подставляет дату/время в формате ISO8601, что почти эквивалентно указанию строки формата даты `"%Y-%m-%d %H:%M:%S"`. Формат ISO8601 также задаёт миллисекунды, которые добавляются к результату использования указанной выше строки формата через запятую. Пример времени в формате ISO8601: `2003-01-23 00:29:50,411`.23392340Запись `class` необязательна. Она указывает имя класса форматировщика (в виде точечного имени модуля и класса). Эта опция полезна для создания экземпляра подкласса [`Formatter`](https://python-all.ru/3.1/library/logging.html#logging.Formatter). Подклассы [`Formatter`](https://python-all.ru/3.1/library/logging.html#logging.Formatter) могут отображать трассировки исключений в расширенном или сжатом формате.23412342### 15.6.20.3. Пример сервера конфигурации23432344Вот пример модуля, использующего сервер конфигурации журналирования:23452346```python2347import logging2348import logging.config2349import time2350import os23512352# Прочитать начальный конфигурационный файл.2353logging.config.fileConfig("logging.conf")23542355# Создать и запустить слушатель на порту 9999.2356t = logging.config.listen(9999)2357t.start()23582359logger = logging.getLogger("simpleExample")23602361try:2362 # Прокрутить вызовы логирования, чтобы увидеть разницу.2363 # Создавать новые конфигурации до нажатия Ctrl+C.2364 while True:2365 logger.debug("debug message")2366 logger.info("info message")2367 logger.warn("warn message")2368 logger.error("error message")2369 logger.critical("critical message")2370 time.sleep(5)2371except KeyboardInterrupt:2372 # очистка2373 logging.config.stopListening()2374 t.join()2375```23762377А вот скрипт, который принимает имя файла и отправляет этот файл на сервер, предварив его двоично-закодированной длиной, в качестве новой конфигурации журналирования:23782379```python2380#!/usr/bin/env python2381import socket, sys, struct23822383data_to_send = open(sys.argv[1], "r").read()23842385HOST = 'localhost'2386PORT = 99992387s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)2388print("connecting...")2389s.connect((HOST, PORT))2390print("sending config...")2391s.send(struct.pack(">L", len(data_to_send)))2392s.send(data_to_send)2393s.close()2394print("complete")2395```23962397## 15.6.21. Дополнительные примеры23982399### 15.6.21.1. Несколько обработчиков и форматировщиков24002401Логгеры – это обычные объекты Python. У метода `addHandler()` нет минимального или максимального ограничения на количество добавляемых обработчиков. Иногда бывает полезно, чтобы приложение записывало все сообщения всех уровней в текстовый файл и одновременно выводило ошибки и выше на консоль. Чтобы это настроить, достаточно сконфигурировать соответствующие обработчики. Вызовы логирования в коде приложения останутся без изменений. Вот небольшая модификация предыдущего простого примера конфигурации на основе модуля:24022403```python2404import logging24052406logger = logging.getLogger("simple_example")2407logger.setLevel(logging.DEBUG)2408# создать файловый обработчик, записывающий даже отладочные сообщения2409fh = logging.FileHandler("spam.log")2410fh.setLevel(logging.DEBUG)2411# создать консольный обработчик с более высоким уровнем логирования2412ch = logging.StreamHandler()2413ch.setLevel(logging.ERROR)2414# создать форматтер и добавить его к обработчикам2415formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")2416ch.setFormatter(formatter)2417fh.setFormatter(formatter)2418# добавить обработчики в логгер2419logger.addHandler(ch)2420logger.addHandler(fh)24212422# код "application"2423logger.debug("debug message")2424logger.info("info message")2425logger.warn("warn message")2426logger.error("error message")2427logger.critical("critical message")2428```24292430Обратите внимание, что код «приложения» безразличен к наличию нескольких обработчиков. Всё, что изменилось – это добавление и настройка нового обработчика с именем *fh*.24312432Возможность создавать новые обработчики с фильтрами более высокого или низкого уровня серьезности может быть очень полезна при написании и тестировании приложения. Вместо использования множества операторов `print` для отладки используйте `logger.debug`: в отличие от операторов print, которые вам придется удалить или закомментировать позже, операторы logger.debug могут оставаться в исходном коде и оставаться неактивными до тех пор, пока они снова не понадобятся. В этом случае единственное, что нужно сделать, – изменить уровень серьезности логгера и/или обработчика на debug.24332434### 15.6.21.2. Использование логирования в нескольких модулях24352436Выше упоминалось, что многократные вызовы `logging.getLogger('someLogger')` возвращают ссылку на один и тот же объект логгера. Это верно не только в пределах одного модуля, но и между модулями, пока они находятся в одном процессе интерпретатора Python. Это справедливо для ссылок на один и тот же объект; кроме того, код приложения может определить и настроить родительский логгер в одном модуле и создать (но не настраивать) дочерний логгер в другом модуле, и все вызовы логгера к дочернему будут передаваться родительскому. Вот пример главного модуля:24372438```python2439import logging2440import auxiliary_module24412442# создать логгер с именем "spam_application"2443logger = logging.getLogger("spam_application")2444logger.setLevel(logging.DEBUG)2445# создать файловый обработчик, записывающий даже отладочные сообщения2446fh = logging.FileHandler("spam.log")2447fh.setLevel(logging.DEBUG)2448# создать консольный обработчик с более высоким уровнем логирования2449ch = logging.StreamHandler()2450ch.setLevel(logging.ERROR)2451# создать форматтер и добавить его к обработчикам2452formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")2453fh.setFormatter(formatter)2454ch.setFormatter(formatter)2455# добавить обработчики к логгеру2456logger.addHandler(fh)2457logger.addHandler(ch)24582459logger.info("creating an instance of auxiliary_module.Auxiliary")2460a = auxiliary_module.Auxiliary()2461logger.info("created an instance of auxiliary_module.Auxiliary")2462logger.info("calling auxiliary_module.Auxiliary.do_something")2463a.do_something()2464logger.info("finished auxiliary_module.Auxiliary.do_something")2465logger.info("calling auxiliary_module.some_function()")2466auxiliary_module.some_function()2467logger.info("done with auxiliary_module.some_function()")2468```24692470А вот вспомогательный модуль:24712472```python2473import logging24742475# создать логгер2476module_logger = logging.getLogger("spam_application.auxiliary")24772478class Auxiliary:2479 def __init__(self):2480 self.logger = logging.getLogger("spam_application.auxiliary.Auxiliary")2481 self.logger.info("creating an instance of Auxiliary")2482 def do_something(self):2483 self.logger.info("doing something")2484 a = 1 + 12485 self.logger.info("done doing something")24862487def some_function():2488 module_logger.info("received a call to \"some_function\"")2489```24902491Вывод выглядит так:24922493```python24942005-03-23 23:47:11,663 - spam_application - INFO -2495 creating an instance of auxiliary_module.Auxiliary24962005-03-23 23:47:11,665 - spam_application.auxiliary.Auxiliary - INFO -2497 creating an instance of Auxiliary24982005-03-23 23:47:11,665 - spam_application - INFO -2499 created an instance of auxiliary_module.Auxiliary25002005-03-23 23:47:11,668 - spam_application - INFO -2501 calling auxiliary_module.Auxiliary.do_something25022005-03-23 23:47:11,668 - spam_application.auxiliary.Auxiliary - INFO -2503 doing something25042005-03-23 23:47:11,669 - spam_application.auxiliary.Auxiliary - INFO -2505 done doing something25062005-03-23 23:47:11,670 - spam_application - INFO -2507 finished auxiliary_module.Auxiliary.do_something25082005-03-23 23:47:11,671 - spam_application - INFO -2509 calling auxiliary_module.some_function()25102005-03-23 23:47:11,672 - spam_application.auxiliary - INFO -2511 received a call to "some_function"25122005-03-23 23:47:11,673 - spam_application - INFO -2513 done with auxiliary_module.some_function()2514```2515