Temporarily change logging level - safely

29 views Asked by At

According to Dynamically changing log level without restarting the application I can temporarily change logging level:

logger_level = my_logger.level
my_logger.setLevel(logging.DEBUG)
a = do_something_with_higher_logging1()
b,c = do_something_with_higher_logging2()
d = do_something_with_higher_logging3()
my_logger.setLevel(logger_level)

The problem is that if do_something_with_higher_logging[123] raise an exception (which is caught outside this, so my program is not terminated), the level of my_logger is not reset back and stays at DEBUG.

I can do

def call_with_loglevel(logger, level, f, **kwargs):
    "Call f with logger at a different level"
    saved_logger_level = logger.level
    logger.setLevel(level)
    try:
        return f(**kwargs)
    finally:
        logger.setLevel(saved_logger_level)

but this requires me to define f out of the do_something_with_higher_logging[123]...

What I want is something like

with my_logger.setLevel(logging.DEBUG):
    a = do_something_with_higher_logging1()
    b,c = do_something_with_higher_logging2()
    d = do_something_with_higher_logging3()
2

There are 2 answers

0
sds On

Making Logger.setLevel return a context manager seems like a backwards-compatible change (should I create an RFE?) but for now it looks like I can do

from contextlib import contextmanager

@contextmanager
def log_level(logger, level):
    "Run body with logger at a different level"
    saved_logger_level = logger.level
    logger.setLevel(level)
    try:
        yield saved_logger_level
    finally:
        logger.setLevel(saved_logger_level)
0
Vinay Sajip On

There's a recipe for this in the Python logging cookbook:

There are times when it would be useful to temporarily change the logging configuration and revert it back after doing something. For this, a context manager is the most obvious way of saving and restoring the logging context. Here is a simple example of such a context manager, which allows you to optionally change the logging level and add a logging handler purely in the scope of the context manager:

import logging
import sys

class LoggingContext:
    def __init__(self, logger, level=None, handler=None, close=True):
        self.logger = logger
        self.level = level
        self.handler = handler
        self.close = close

    def __enter__(self):
        if self.level is not None:
            self.old_level = self.logger.level
            self.logger.setLevel(self.level)
        if self.handler:
            self.logger.addHandler(self.handler)

    def __exit__(self, et, ev, tb):
        if self.level is not None:
            self.logger.setLevel(self.old_level)
        if self.handler:
            self.logger.removeHandler(self.handler)
        if self.handler and self.close:
            self.handler.close()
        # implicit return of None => don't swallow exceptions