Иерархия Python logger и корневой logger при входе в систему с несколькими модулями - PullRequest
3 голосов
/ 01 октября 2019

У меня есть эта настройка:

main.py
/module
/module/__init__.py (empty)
/module.py

А вот код для моих двух файлов, main.py и module.py соответственно:

main.py

import logging
from module import module

logger = logging.getLogger(__name__)

def test():
    logger.warning('in main.py/test')

def main():
    handler = logging.StreamHandler()
    handler.setLevel(logging.INFO)
    formatter = logging.Formatter('%(asctime)s %(name)s/%(module)s [%(levelname)s]: %(message)s', '%Y-%m-%d %H:%M:%S')
    handler.setFormatter(formatter)
    logger.addHandler(handler)

    logger.warning('in main.py/main')
    module.something()

if __name__ == "__main__":
    main()    

module.py

import logging
logger = logging.getLogger(__name__)

def something():
    logger.warning('in module.py/something')

Итак, я заметил, что это выдает следующее (обратите внимание, что в модуле регистрации нет форматирования):

2019-10-01 09:03:40 __main__/main [WARNING]: in main.py/main
in module.py/something

Похоже, что только после того, как я сделаю правку в main.py, изменим logger = logging.getLogger( __ name __ ) на logger = logging.getLogger() или добавим logger = logging.getLogger() после def main():, чтобы он регистрировалсявот так (что я и хочу):

2019-10-01 09:04:13 root/main [WARNING]: in main.py/main
2019-10-01 09:04:13 module.module/module [WARNING]: in module.py/something

Почему это так? Я думал, что, поскольку main.py импортирует module.py, он, естественно, выше в иерархической шкале, поэтому module.py наследует настройки регистратора, как определено в main.py. Нужно ли явно устанавливать корневой логгер (с logger = logging.getLogger()) в main, чтобы наследование работало? Не правильно ли я настроил структуру папок, чтобы логгер module.py наследовал настройки логгера main.py, или структура папок не имеет значения?

Причина, по которой я спрашиваю, заключается в том, что я подумал, что следует использовать logger = logging.getLogger( __ name __ ) повсюду (даже в main.py), а затем на основе структуры импорта (или структуры папок?), которая определит иерархию, и средства ведения журнала будут наследоваться соответственно. И причина, по которой я сделал это предположение, в том, что, если бы я импортировал main.py в другую программу? Я предполагаю, что я хочу сделать ведение журнала как можно более универсальным, чтобы я мог импортировать один модуль в другой, и он всегда наследует настройки регистратора родителя. Есть ли способ отобразить основную иерархию всех модулей для целей отладки / обучения?

1 Ответ

3 голосов
/ 04 октября 2019

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

В вашем примере настройка ведения журналов больше связана с последовательностью выполнения иимена, которые вы выбрали, чем что-либо еще. Когда ваша программа работает, она выполняет следующее:

  1. Запускает logging.py из стандартной библиотеки из-за import logging
  2. Запускает module.py для выполнения from module import module
  3. Устанавливает атрибут logger в main для Logger с именем __main__.
  4. Создать test функцию
  5. Создать main функцию
  6. Запустить основную функцию

Некоторые последствия этой последовательности событий:

  • module.logger создано до main.logger. Это не влияет на поведение, которое вы видите, но это стоит отметить в данных обстоятельствах.
  • main.logger называется __main__, если вы вызываете main в качестве сценария. Поведение, которое вы видите, не изменилось бы, если бы оно называлось main, например, из python -m main.
  • module явно не в той же иерархии, что и main. Оба являются потомками корневого логгера по разным веткам.

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

Вы можете сделать так, чтобы все логгеры наследовали от main. В module/module.py вы бы сделали

logger = logging.getLogger('__main__.' + __name__)

Проблема здесь в том, что имя __main__ жестко запрограммировано. У вас нет гарантии, что это будет __main__ против main. Вы можете попробовать import main в module, чтобы вы могли сделать main.__name__ + '.' + __name__, но это не сработает, как ожидалось. Если main был запущен как __main__, то при его импорте будет создан второй объект модуля с совершенно отдельной иерархией ведения журнала.

Именно поэтому корневой регистратор не имеет имени. Это обеспечивает именно ту ремонтопригодность и согласованность, которые вы хотите. Вам не нужно прыгать через обручи, пытаясь выяснить корневое имя.

При этом вам все равно нужно main.py войти в систему __main__ или main. Корневой регистратор должен быть настроен только в защите импорта. Таким образом, если main импортируется как обычный модуль, он будет учитывать настройки ведения журнала драйвера, под которым он работает.

TL; DR

ItОбычно для установки анонимного корневого регистратора в драйвере вашей программы. Не пытайтесь наследовать регистраторы от __main__ или от имени модуля драйвера.

...