Простой Logging в Python
Для логирования действий в python есть модуль logging.
Здесь во всех примерах логирование будет просто выводом в консоль, по сути, как настраиваемые отладочные сообщения, но сам модуль позволяет делать гораздо больше.
В целом кратко:
- Создается объект логгер
- Устанавливается уровень сообщений, которые он будет воспринимать
- Создаются хендлер(ы), которые будут заниматься выводом логов (в файл, в консоль...)
- Устанавливается уровень вывода для каждого хендлера (например, в консоль могут выводиться только ошибки, а в файл записываться ошибки и предупреждения).
- Создаются форматтеры (определяют формат вывода сообщений в логи)
- Форматтеры привязываются к хендлерам
- Хендлеры привязываются к логгерам (один логгер может иметь сразу несколько разных хендлеров одновременно, например, для вывода и в файл и в консоль).
Само собой всё это гибко настраивается и расширяется/дополняется при необходимости.
Пример начальной инициализации:
import logging # создание root логгера logger = logging.getLogger() # установка уровня логирования для всего логгера logger.setLevel(logging.INFO) # создание формата для вывода сообщений formatter = logging.Formatter("%(levelname)s: %(name)s: %(message)s") #formatter = logging.Formatter("%(filename)s:%(lineno)d: %(name)s %(funcName)s: %(message)s") # создание StreamHandler - то есть потокового вывода handler = logging.StreamHandler() # установка уровня логирования для текущего хендлера # (может отличаться от уровня для всего логгера) handler.setLevel(logging.INFO) # Привязка ранее созданного формата к хендлеру handler.setFormatter(formatter) # Привязка ранее созданного хендлера к логгеру logger.addHandler(handler)
Вывод в лог сообщений разного уровня:
logger.critical("All is lost and nothing can be done."). logger.error("Unable to open input file %s.", infile). logger.warning("Config file not found. Using defaults"). logger.info("SSL verify disabled") logger.debug("id=%d, name=%s, full_name=%s, url=%s", user_id, user_name, user_fname, url)
Передавать созданный логгер, например, в классы или подмодули не обязательно. Можно одной строкой создать дочерний логгер и он будет выводить через родительский, со всеми его привязками и настройками, без дополнительных настроек (само собой, при желании можно часть или все настройки поменять, не затрагивая родительский).
Создать дочерний логгер с заданным именем:
class NewShinyClass(): def __init__(self): # создание дочернего логгера в классе self.logger = logging.getLogger('pygogs') def some_member(self): ... self.logger.debug("Entering advanced mode") ...
Если надо сделать условие на текущий реальный уровень логгирования (с учетом родительско-дочерних отношений логгеров) то так:
if self.logger.getEffectiveLevel() < logging.INFO: self.logger.debug("Scan source dirs...:") self.do_scan() self.logger.debug("Scan complete")
Если скрипт является консольным приложением с поддержкой аргументов командно строки, то можно сделать (как это обычно принято у консольных приложений) аргумент verbose или -v и чем больше таких аргументов, тем подробнее вывод.
В примере разбор аргументов сделан при помощи типового argparse.
#!/usr/bin/env python3 # -*- coding: utf-8 -*- import argparse import logging __version__ = "1.0" ############################################################################### def main(): descr = "My super script" additional = "just example" parser = argparse.ArgumentParser (description=descr, epilog=additional) parser.add_argument('--version', action='version', version='%(prog)s ' + __version__) parser.add_argument("-v", "--verbose", action="count", default=0, help="increase verbosity") args = parser.parse_args() # уровень логгирования в зависимости от количества параметров -v if args.verbose == 0: lvl = logging.WARNING elif args.verbose == 1: lvl = logging.INFO else: lvl = logging.DEBUG logger = logging.getLogger() logger.setLevel(lvl) formatter = logging.Formatter("%(levelname)s: %(message)s") handler = logging.StreamHandler() handler.setLevel(lvl) handler.setFormatter(formatter) logger.addHandler(handler) ..... return 0 ############################################################################### if __name__ == '__main__': ret = main() sys.exit(ret)
Отличное подробное видео на тему: https://youtu.be/jF_XglX0JgM