Как использовать контекстные менеджеры для временного изменения уровня логирования?

Контекстные менеджеры позволяют временно изменить уровень логирования, гарантируя его восстановление после завершения блока `with`. Пример:

import logging

class LogLevelContext:
    def __init__(self, logger, level):
        self.logger = logger
        self.level = level
        self.original_level = logger.level

    def __enter__(self):
        self.logger.setLevel(self.level)

    def __exit__(self, exc_type, exc_val, exc_tb):
        self.logger.setLevel(self.original_level)


logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)

with LogLevelContext(logger, logging.DEBUG):
    logger.debug("Это отладочное сообщение, видимое только внутри контекста.")
    logger.info("Это информационное сообщение.")

logger.info("Это информационное сообщение, уровень логирования восстановлен.")
В `__enter__` мы меняем уровень логирования, а в `__exit__` возвращаем его обратно.

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

Вот пример реализации контекстного менеджера для временного изменения уровня логирования:


import logging

class LoggingLevelContext:
    def __init__(self, logger, level):
        self.logger = logger
        self.level = level
        self.original_level = logger.level  # Store the original level

    def __enter__(self):
        self.logger.setLevel(self.level)
        return self.logger

    def __exit__(self, exc_type, exc_val, exc_tb):
        self.logger.setLevel(self.original_level)  # Restore the original level

Объяснение кода:

  • __init__(self, logger, level): Конструктор, принимает экземпляр логгера и новый уровень логирования. Сохраняет исходный уровень логгера в self.original_level.
  • __enter__(self): Вызывается при входе в блок with. Устанавливает новый уровень логирования для указанного логгера. Возвращает экземпляр логгера (необязательно, но удобно для дальнейшего использования внутри блока with).
  • __exit__(self, exc_type, exc_val, exc_tb): Вызывается при выходе из блока with (независимо от того, было ли исключение). Восстанавливает исходный уровень логирования, сохраненный в self.original_level. Параметры exc_type, exc_val и exc_tb содержат информацию об исключении, если оно произошло, но в данном случае они не используются.

Пример использования:


logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)  # Set a default logging level

# Add a handler to the logger (e.g., a StreamHandler to print to the console)
handler = logging.StreamHandler()
logger.addHandler(handler)

logger.info("This message will be logged at the INFO level.")
logger.debug("This message will NOT be logged because the logger's level is INFO.")


with LoggingLevelContext(logger, logging.DEBUG):
    logger.debug("This message WILL be logged because the level is temporarily DEBUG.")
    logger.info("This message will ALSO be logged because DEBUG is lower than INFO.")

logger.info("This message will be logged again at the INFO level.")
logger.debug("This message will NOT be logged because the logger's level is back to INFO.")

Альтернативный подход (использование logging.disable):

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


import logging

class DisableLoggingContext:
    def __init__(self, disable_level=logging.CRITICAL + 1):  # Default: disable everything
        self.disable_level = disable_level
        self.previous_disable = logging.disable

    def __enter__(self):
        self.previous_disable = logging.disable
        logging.disable(self.disable_level)

    def __exit__(self, exc_type, exc_val, exc_tb):
        logging.disable(self.previous_disable)

Пример использования DisableLoggingContext:


import logging

logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
handler = logging.StreamHandler()
logger.addHandler(handler)


logger.info("This message will be logged.")

with DisableLoggingContext(logging.ERROR): # Disable logging of INFO, WARNING, DEBUG
    logger.info("This message will NOT be logged.")
    logger.error("This message WILL be logged because logging.ERROR is not disabled.")


logger.info("This message will be logged again.")

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

0