Обратите внимание, что объекты регистраторов Logger
никогда не должны создаваться напрямую, а должны всегда создаваться через функцию уровня модуля logging.getLogger(name)
. Многократные вызовы logging.getLogger()
с одним и тем же именем всегда возвращают ссылку на один и тот же объект Logger
.
Имя name
может представлять собой иерархическое значение, разделенное точками, например foo.bar.baz
хотя, оно также может быть просто foo
. Регистраторы, которые находятся ниже в иерархическом списке, являются потомками регистраторов, которые находятся выше в списке. Например, при наличии регистратора Logger
с именем foo
, регистратор с именами foo.bar
, foo.bar.baz
и foo.bam
являются потомками foo
. Иерархия имен регистраторов аналогична иерархии пакетов Python и будет идентична ей, если организовывать регистраторы для каждого модуля с использованием рекомендуемой конструкции logging.getLogger(__name__)
, где __name__
имя модуля.
Logger
имеют следующие атрибуты и методыLogger
logger.propagate
,logger.setLevel()
,logger.isEnabledFor()
,logger.getEffectiveLevel()
,logger.getChild()
,DEBUG
logger.debug()
,INFO
logger.info()
,WARNING
logger.warning()
,ERROR
logger.error()
,CRITICAL
logger.critical()
,logger.log()
,logger.exception()
,logger.addFilter()
,logger.removeFilter()
,logger.filter()
,logger.addHandler()
,logger.removeHandler()
,logger.findCaller()
,logger.handle()
,LogRecord
logger.makeRecord()
,logger.hasHandlers()
,logger.propagate
:Если атрибут logger.propagate
имеет значение True
, события, записанные в этот регистратор, будут передаваться обработчикам регистраторов более высокого уровня (предка), в дополнение к любым обработчикам, подключенным к этому регистратору. Сообщения передаются непосредственно обработчикам регистраторов предков - ни уровень, ни фильтры регистраторов предков не срабатывают на это сообщение.
Если атрибут logger.propagate
имеет значение False
, сообщения регистрации не передаются обработчикам регистраторов предков.
Конструктор устанавливает для этого атрибута значение True
.
Примечание. Если присоединить обработчик к регистратору Logger
и одному или нескольким его предкам, то он может отправлять одну и ту же запись несколько раз. В общем, не нужно подключать обработчик к нескольким регистраторам. Достаточно присоединить его к корневому регистратору, который является самым высоким в иерархии и он увидит все события, записанные всеми регистраторами-потомками, при условии, что они будут передаваться. Для параметра logger.propagate
оставлено значение True
. Распространенный сценарий использования - это подключить обработчики только к корневому регистратору Logger
и позволить распространению позаботиться обо всем остальном.
logger.setLevel(level)
:Метод logger.setLevel()
устанавливает уровень level
порога логирования для этого регистратора. Регистрация сообщений, которые менее серьезны, чем указанный уровень, будет игнорироваться. Сообщения, имеющие такой же порог логирования или выше, будут отправляться любыми обработчикам, если уровень в обработчике не был установлен на более высокий уровень логирования, чем уровень level
регистратора.
При создании регистратора уровень устанавливается равным NOTSET
, что приводит к обработке всех сообщений, когда регистратор является корневым или делегированию родительскому элементу, когда регистратор не является корневым регистратором. Обратите внимание, что корневой Logger
создается с уровнем WARNING
.
Термин "делегирование родителю" означает, что если у регистратора есть уровень NOTSET
, его цепочка регистраторов предков пересекается, пока не будет найден либо предок с уровнем, отличным от NOTSET
, либо пока не будет достигнут корень.
Если предок обнаружен с уровнем, отличным от NOTSET
, то уровень этого предка рассматривается как эффективный уровень средства ведения журнала, с которого начался поиск предка и используется для определения способа обработки события ведения журнала.
Если корень достигнут и имеет уровень NOTSET
, то все сообщения будут обработаны. В противном случае, как эффективный уровень будет использоваться уровень корня.
logger.isEnabledFor(level)
:Метод logger.isEnabledFor()
указывает, будет ли сообщение уровня level
обрабатываться этим регистратором. Этот метод сначала проверяет уровень level
модуля, установленный с помощью функции logging.disable(level)
, а затем эффективный уровень регистратора, определенный методом logger.getEffectiveLevel()
.
logger.getEffectiveLevel()
:Метод logger.getEffectiveLevel()
возвращает эффективный уровень логирования для этого регистратора, если значение, отличное от logging.NOTSET
, было задано с помощью функции logger.setLevel()
. В противном случае иерархия перемещается к корню, пока не будет найдено значение, отличное от logging.NOTSET
и это значение будет возвращено. Возвращаемым значением является целое число, обычно одно из logging.DEBUG
, logging.INFO
и т. д.
logger.getChild(suffix)
:Метод logger.getChild()
возвращает регистратор, который является потомком этого регистратора, как определено суффиксом. Таким образом, logging.getLogger('abc').GetChild('def.ghi')
вернет тот же регистратор, который будет возвращен logging.getLogger('abc.def.ghi')
. Этот метод становится особенно полезным, когда имя родительского логгера называется, например с помощью переменной __name__
, а не как буквальная строка.
logger.debug(msg, *args, **kwargs)
:Метод logger.debug()
регистрирует сообщение msg
с уровнем logging.DEBUG
на этом регистраторе Logger
.
Аргумент msg
- это строка формата сообщения, а *args
- аргументы, которые объединяются в msg
с помощью оператора форматирования строки. Обратите внимание, это означает, что вы можете использовать ключевые аргументы в строке формата вместе с одним аргументом словаря.
Операция форматирования в стиле printf
%
не выполняется для msg
, если не предоставлены аргументы *args
.
В **kwargs проверяются четыре ключевых аргумента: exc_info
, stack_info
, stacklevel
и extra
:
Подробное описание и поведение аргументов exc_info
, stack_info
и extra
смотрите в документации по функциям регистрации сообщений.
Необязательный ключевой аргумент stacklevel
представляет собой уровень стека, который по умолчанию равен 1. Если stacklevel
больше 1, то соответствующее количество кадров стека пропускается при вычислении номера строки и имени функции, заданных в LogRecord
. Это может быть использовано при ведении журнала помощников, так что записанное имя функции, имя файла и номер строки являются не информацией для вспомогательной функции/метода, а скорее ее вызывающей стороной. Имя этого параметра отражает эквивалент модуля warnings
.
Изменено в Python-3.8: добавлен параметр уровня стека stacklevel
.
logger.info(msg, *args, **kwargs)
:Метод logger.info()
регистрирует сообщение msg
с уровнем INFO
на этом регистраторе.
Аргументы интерпретируются как для метода logger.debug()
.
logger.warning(msg, *args, **kwargs)
:Метод logger.warning()
регистрирует сообщение msg
с уровнем WARNING
на этом регистраторе.
Аргументы интерпретируются как для метода logger.debug()
.
Примечание. Существует устаревший метод logger.warn
, который функционально идентичен logger.warning()
. Не стоит использовать устаревший метод.
logger.error(msg, *args, **kwargs)
:Метод logger.error()
регистрирует сообщение msg
с уровнем ERROR
на этом регистраторе.
Аргументы интерпретируются как для метода logger.debug()
.
logger.critical(msg, *args, **kwargs)
:Метод logger.critical()
регистрирует сообщение msg
с уровнем CRITICAL
на этом регистраторе.
Аргументы интерпретируются как для метода logger.debug()
.
logger.log(level, msg, *args, **kwargs)
:Метод logger.log()
регистрирует сообщение msg
с целочисленным уровнем level
на этом регистраторе.
Аргументы интерпретируются как для метода logger.debug()
.
logger.exception(msg, *args, **kwargs)
:Метод logger.exception()
регистрирует сообщение msg
с уровнем ERROR
на этом регистраторе.
Аргументы интерпретируются как для метода logger.debug()
.
Информация об исключении добавляется в сообщение регистрации msg
. Этот метод должен вызываться только из обработчика исключений.
logger.addFilter(filter)
:Метод logger.addFilter()
добавляет указанный фильтр filter
в этот регистратор Logger
.
logger.removeFilter(filter)
:Метод logger.removeFilter()
удаляет указанный фильтр filter
из этого регистратора.
logger.filter(record)
:Метод logger.filter()
применяет фильтры этого регистратора к записи и возвращает True
, если запись должна быть обработана.
Фильтры применяются по очереди, пока один из них не возвращает ложное значение. Если ни один из них не возвращает ложное значение, запись будет обработана, т.е. передана обработчикам. Если возвращается ложное значение, дальнейшая обработка записи не происходит.
logger.addHandler(hdlr)
:Метод logger.addHandler()
добавляет указанный обработчик hdlr
в этот регистратор.
logger.removeHandler(hdlr)
:Метод logger.removeHandler()
удаляет указанный обработчик hdlr
из этого регистратора.
logger.findCaller(stack_info=False, stacklevel=1)
:Метод logger.findCaller()
находит исходное имя вызывающего абонента и номер строки. Возвращает имя файла, номер строки, имя функции и информацию о стеке в виде 4-элементного кортежа. Если аргумент stack_info=False
, то информация стека возвращается как None
.
Параметр stacklevel
передается из кода, вызывающего методами logger.debug()
, logger.info()
и т. д. Если stacklevel
больше 1, то пропускается соответствующее количество кадров стека перед определением значений, которые будут возвращены. Как правило, это будет полезно при вызове API журналирования с помощником/оберткой, так что информация в журнале событий будет относится не к помощнику/обертке, а к коду, который ее вызывает.
logger.handle(record)
:Метод logger.handle()
обрабатывает запись record
, передавая ее всем обработчикам, связанным с этим регистратором и его предками пока не будет найдено ложное значение распространения. Этот метод используется для записей без засечек, полученных из сокета, а также для записей, созданных локально. Фильтрация на уровне Logger
применяется с помощью метода logger.filter()
.
logger.makeRecord(name, level, fn, lno, msg, args, exc_info, func=None, extra=None, sinfo=None)
:Метод logger.makeRecord()
представляет собой фабричный метод, который может быть переопределен в подклассах, чтобы создавать специализированные объекты LogRecord
.
logger.hasHandlers()
:Метод logger.hasHandlers()
проверяет, настроены ли в этом регистраторе Logger
какие-либо обработчики. Это делается путем поиска обработчиков в этом регистраторе и его родителях в иерархии регистратора.
Возвращает True
, если обработчик был найден, иначе False
. Метод прекращает поиск иерархии всякий раз, когда обнаруживается регистратор с атрибутом logger.propagate
, установленным в False
- это будет последний регистратор, который проверяется на наличие обработчиков.
Код приложения может определять и настраивать корневой регистратор в основном модуле и создавать, но не настраивать дочерний регистратор в отдельном модуле, и все вызовы регистратора для дочернего объекта будут передаваться родительскому.
Код основного модуля:
# test.py import logging import aux # создаем регистратор с именем 'logger' logger = logging.getLogger('log') logger.setLevel(logging.DEBUG) # создаем файловый обработчик, который # регистрирует отладочные сообщения fh = logging.FileHandler('spam.log') fh.setLevel(logging.DEBUG) # создаем консольный обработчик # с более высоким уровнем журнала ch = logging.StreamHandler() ch.setLevel(logging.ERROR) # создаем форматтер и добавляем его в обработчики fmtstr = '%(asctime)s - %(name)s - %(levelname)s - %(message)s' fmtdate = '%H:%M:%S' formatter = logging.Formatter(fmtstr, fmtdate) # создаем форматтер в обработчики fh.setFormatter(formatter) ch.setFormatter(formatter) # добавляем настроенные обработчики в логгер logger.addHandler(fh) logger.addHandler(ch) # вызов функций, регистрирующих # события в коде logger.info('создание объекта Auxiliary') a = auxiliary.Auxiliary() logger.info('объект Auxiliary создан') logger.info('вызов метода .something') a.something() logger.info('вызов .something закончен') logger.info('вызов some_func()') auxiliary.some_func() logger.info('работа с Auxiliary закончена')
Для приложения будет полезно записывать все сообщения всех уровней логирования в текстовый файл, одновременно регистрируя уровень ERROR
или выше в консоли. Что бы настроить такое поведение, необходимо определить соответствующий обработчик handler
как показано в примере выше. Класс logger.addHandler()
не имеет минимальной или максимальной квоты для количества добавляемых обработчиков.
Код импортируемого модуля.
# aux.py import logging # создание регистратора с именем log.aux module = logging.getLogger('log.aux') class Auxiliary: def __init__(self): # создание регистратора с именем log.aux.Auxiliary self.logger = logging.getLogger('log.aux.Auxiliary') self.logger.debug('__Init__ Auxiliary') def something(self): self.logger.debug('start .something') a = 1 + 1 self.logger.debug('end .something') def some_func(): module.debug('call to some_func')
Обратите внимание на имена регистраторов, создаваемых в импортируемом модуле. Имена должны соблюдать иерархию, т. е. в данном случае, в модуле aux.py
имя регистратора должно начинаться с имени корневого регистратора 'log.'
. Передаваемое имя в logging.getLogger()
представляет собой ссылку на объект регистратора и если getLogger()
не находит объекта с соответствующим именем, то он считает себя корневым регистратором. Другими словами, если не соблюдать это правило, то соответствующие сообщения не будут записываться в файл или выводится на консоль.
Вывод будет выглядеть так:
08:52:46 - log - INFO - создание объекта Auxiliary 08:52:46 - log.aux.Auxiliary - DEBUG - __Init__ Auxiliary 08:52:46 - log - INFO - объект Auxiliary создан 08:52:46 - log - INFO - вызов метода .something 08:52:46 - log.aux.Auxiliary - DEBUG - start .something 08:52:46 - log.aux.Auxiliary - DEBUG - end .something 08:52:46 - log - INFO - вызов .something закончен 08:52:46 - log - INFO - вызов some_func() 08:52:46 - log.aux - DEBUG - call to some_func 08:52:46 - log - INFO - работа с Auxiliary закончена
Возможность создания новых обработчиков с фильтрами более высокой или более низкой важности может быть очень полезна при написании и тестировании приложения. Вместо использования функций print()
для отладки используйте метод logger.debug()
: в отличие от операторов print()
, которые придется удалить или закомментировать позже, функции logger.debug()
могут оставаться нетронутыми в исходном коде и оставаться неактивными, пока они вам не понадобятся снова. Единственное изменение, которое необходимо сделать - это изменить уровень логирования и/или обработчика для отладки.