Чтобы правильно использовать модуль logging, нужно во всех своих модулях содавать логгеры следующим образом:
import logging
log = logging.getLogger(__name__)
В модулях, которые не запускаются сами по себе, никакого другого кода инициализации и настройки логирования не требуется.
Если вы в своём модуле желаете различать два или более вида логов, то можно сделать как-то так:
log = logging.getLogger(__name__)
class MyModel:
log = logging.getLogger(__name__ + '.MyModel')
# ...
В этом случае для этого класса будет свой логгер со своим именем.
Также логгер можно создавать в метаклассе давать ему имя класса автиоматически, тогда каждый потомок будет иметь свой логгер со своим именем.
Однако все эти логгеры ничего не знают о файлах и потоках, куда будут попадать их логи. Все фильтрации и перенаправления будут делаться в единой конфигурации логгирования в основной программе.
В главном запускаемом файле кроме обычного создания логгера как во всех модулях будет ещё и инициализация и настройка системы логирования.
В самом простом виде это что-то вроде такого:
if __name__ == '__main__':
logging.basicConfig(stream=sys.stderr, level='INFO', format='%(asctime)s %(levelname)-7s %(message)s')
Тут говорится, что все логи уровня >= INFO будут в указанном формате направлены в stderr.
В более сложном случае вы можете загрузить настройку логирования из конфигурационного файла или описать кодом:
if __name__ == '__main__':
# У вас может быть несколько разных способов форматировать код для разных мест:
formatter_simple = Formatter(u'%(relativeCreated)08d %(levelname)-7s %(message)s')
formatter_complex = Formatter(u'%(asctime)s %(levelname)-7s [%(filename)21s:%(lineno)-4d] %(message)s')
# Несколько разных хендлеров для перехвата нужного вида сообщений и отправки в правильное место:
handler_null = logging.NullHandler()
handler_screen = handler(fmt=formatter_simple, stream=sys.stderr)
handler_main_file = handler(
fmt=formatter_complex,
cls=logging.handlers.TimedRotatingFileHandler,
when='midnight',
backupCount=5,
encoding='utf-8',
filename=local_path('log/server.log'),
)
handler_errors_file = handler(
fmt=formatter_complex,
cls=logging.handlers.TimedRotatingFileHandler,
when='midnight',
backupCount=5,
encoding='utf-8',
filename=local_path('log/errors.log'),
level='ERROR',
)
# А потом описываем сами логгеры:
# это корневой логер, пропускает все сообщения через себя насквозь и в то же отдаёт их своим хендлерам
log_root = logger(None, level='DEBUG', propagate=1, handlers=[handler_errors_file, handler_main_file, handler_screen])
# этот логер перехватывает логи торнадо-приложения, пропускает через себя и отдаёт хендлерам:
log_app = logger('tornado.application', level='DEBUG', propagate=1, handlers=[handler_errors_file, handler_main_file, handler_screen])
# этот собирает логи событий модели уровня выше или равного INFO (не отладочные)
# и отдаёт соответствующим хендлерам, дальше для обработки свои сообщения не пускает
log_events = logger('app.model.events', level='INFO', propagate=0, handlers=[handler_errors_file, handler_events_file])
# этот логер съедает и отдаёт спец-хендлеру (он не показан выше, но должен быть) все логи http доступа
log_websrv = logger('tornado.access', level='DEBUG', propagate=0, handlers=[handler_websrv_file])
# этот логер глотает и гасит за ненадобностью все логи, которые генерит библиотека PIL при работе с PNG-файлами
log_pil = logger('PIL.PngImagePlugin', level='INFO', propagate=0, handlers=[handler_null])
Если сообщение не перехвачено и не остановлено специфичным логером, оно улетает корневому логеру.
Ну примерно как-то так.
Само собой всю эту настройку лучше вынести в отдельный модуль и запускать при старте главного файла.
Логеры вроде
log_pil
,
log_websrv
и прочих сохранены в отдельные переменные, но по факту в коде эти переменные нигде не используются. Их можно и не присваивать вовсе. В модуле логирования все логгеры регистрируются глобально в списке и поэтому в каждом модуле не требуется ничего импортировать, достаточно создать логгер по имени. По этому имени логер ищется среди созданных или автоматом создаётся новый. Имена логгеров используют точечную нотацию, и по ступеням имени их удобно фильтровать.
Такой подход имеет свои недостатки (синглтон, неявное описание, толерантность к ошибкам), достоинства его весомы: отсутствие ада зависимостей (когда непросто или невозможно определить ациклический порядок создания логеров), гибкость настройки, изоляция (возможность перекрытия и фильтрации не влезая в код логирования).
P.S.
Старайтесь не использовать CamelCase в именовании файлов проекта. Не смотря на то, что негласное это правило часто нарушается даже популярными библиотеками, всё же это часто создаёт лишнюю неразбериху и проблемы.
В частности много геморроя можно поиметь переименовав файл сменив лишь регистр символа под контролем версий при работе с репозиторием на разных файловых системах. В винде, к примеру, регистр в именах сохраняется, но системой не различается, а в линукс различается. Таким образом винда не увидит переименования, а линукс увидит. Это может породить адский треш.