Logging

Logging

@zartsoft

Zart, [29.06.17 21:27]

логгинг состоит из пачки классов:


логгер Logger - источник сообщений. тот класс, у которого есть метод log, который шлёт сообщение в логгер с указанными данными и приоритетом.

у него так же есть вспомогательные методы debug/fatal/critical/info/warn/warning/error/exception, которые вызывают log с указанным уровнем.

Этот метод создает LogRecord объект, который содержит информацию о сообщении,его аргументах, отладочной инфе и экстра данных.


у логгера есть уровень (getLevel/setLevel методы), который может отфильтровать сообщения еще перед их отправкой - т.е. если у логгера например уровень ошибок, то отладочные сообщения и ворнинги будут проигнорированы сразу.


имена логгеров разделяются точками и имеют иерархию - например, отправка сообщений в myprogram.engine.mysql будет проходить по логгерам myprogram.engine.mysql, myprogram.engine, myprogram, root.


У логгера есть аттрибут propagate, по умолчанию равный True, который можно переключить в False, если нужно отключить всплытие по иерархии.


Логгеры являются синглетонами, изначально есть лишь один рутовый, с пустым '' именем. Когда программа дергает логгер через logging.getLogger, возвращается уже существующий экземпляр логгера, если есть, или создается новый (а также промежуточные выше).

Поэтому если хочется разные части программы логгировать по разному - необходимо задавать разные имена.


К каждому логгеру можно прикрепить 0 и более хендлеров. Это классы, производные от LogHandler, которые занимаются доставкой сообщений.


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

Можно создавать собственные хендлеры для своих нужд.


У каждого хендлера тоже есть уровень, с помощью которого можно фильтровать сообщения по их уровню перед записью.


К хендлеру привязан форматтер - класс, который хавает логрекорд и конвертирует его в строку текста для финальной записи (стандартный форматтер еще имеет и строку форматирования времени).

В двойке форматтер умел конвертировать лишь %(name)s строки, в тройке добавили варианты с новым '{name}' форматом.


Zart, [29.06.17 21:27]

Использование:


Из обзора выше видно что всё это богатство можно поделить на две части:

для программистов (библиотек и кода вообще) - часть, занимающаяся отправкой.

для администраторов/конечных пользователей - часть, настраивающая доставку.



как применять логгинг в типичном коде - получить логгер, и отправить в него сообщение:


  import logging

   

  # использование имени модуля для логов очень популярная техника

  # очень хорошо работает когда исходники выстроены в стройную иерархию

  log = logging.getLogger(__name__)

   

  def foo(x):

    log.debug('отладочная инфа...', bar, baz)

     

    ...

    try:

      fuckup(x)

    except SomeError as e:

      log.exception('Shit happens') # exception вызывает log(..., exc_info=True),

                     # который сохраняет исключение и трейсбак в логрекорде

      


  class Foo:

    log = logging.getLogger('Foo') # иногда логгеры объявляются на уровне класса

     

    def method(self):

      self.log.warn('Deprecated method "method"')

      ...

   

   

  class Bar:

    def __init__(self):

      self.log = logging.getLogger('Bar') # можно на уровне инстанса

      self.log.debug('Bar.__init__(%r)', self)

     

    def demo(self):

      log = logging.getLogger('Bar') # а можно создавать по необходимости

      log.critical('MAYDAY')


Zart, [29.06.17 21:27]

Настройка:


Примеры кода выше - это то, как вшить в программу генерацию логов.

Теперь встает вопрос, как настроить собсно их вывод. Из описания выше, очевидно, что самый простой способ - это задать один хендлер у рутового логгера, который будет ловить все сообщений и выводить их на экран/файл.


Именно это и делает logging.basicConfig - задает рутовому логгеру один хендлер, либо для вывода в консоль, либо в указанный файл.


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


Для более гибкой настройки логгинга есть модуль logging.config с пачкой методов, которые занимаются созданием хендлеров и прикручиванием их к логгерам.


Это logging.config.fileConfig, который настраивает логи через конфиг в ini-файле,

logging.config.dictConfig, настраивающий логгинг через дикт, и 

logging.config.listen, который умеет открывать tcp-порт и слушать команды на реконфигурацию логов на ходу и по командам через сеть.


Zart, [29.06.17 21:27]

Важные аспекты:


Логгеры - синглетоны.

Все настройки логгеров и хендлеров глобальны на уровне процесса.

Нельзя настроить разный логгинг в разных тредах.

Реконфигурация через fileConfig НЕ УДАЛЯЕТ старых хендлеров, но по дефолту их отключает.

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

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

Конфигурацию логгинга должны делать ТОЛЬКО программы и делать это на запуске.

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

Я не упомянул еще ряда мелочей - подробности в документации и коде.

Report Page