Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Show the original function when logging funcName in a decorator

I have a decorator to retry some operations for 3 times if a deadlock happens, and does some logging in the process:

def retry_on_deadlock(func):
    logger = logging.getLogger('test')

    @wraps(func)
    def decorated(*args, **kwargs):
        retry_count = 0

        while retry_count < 3:
            try:
                return func(*args, **kwargs)
            except DeadLockException:
                retry_count += 1

                if retry_count == 3:
                    raise

                logger.warning('We are in the decorated function here')

    return decorated

@retry_on_deadlock
def crucial_function(value):
    with value:
        do_something()

crucial_function(important_data)

Our logging format includes %(funcName)s which, in this specific case, will evaluate to decorated. Is there a way to make crucial_function appear in the logs instead?

I already tried implementing a logging filter, but the logic it needs became a bit cumbersome, as it needs to inspect the stack, and if the logging happened directly in the decorated function (ie. not in a function called by the original function) it will overwrite funcName on the log record. If this is the only way, though, i will sadly accept it.

Update: This is not the same as this question as i don’t really care about the functions signature (which, in fact, is preserved by the @wraps decorator). I want to instruct the logging library to skip one level from the stack trace when logging the function’s name via the %(funcName)s, %(filename)s, and %(lineno)s.

like image 994
GergelyPolonkai Avatar asked Sep 13 '25 12:09

GergelyPolonkai


1 Answers

Just now face this problem while writing a logging wrapper. And accidentally found a solution.

As of Python3.8 you can now use stacklevel keyword which is available in all logging methods (debug, info, warning, error, critical)

From docs

The third optional keyword argument is stacklevel, which defaults to 1. If greater than 1, the corresponding number of stack frames are skipped when computing the line number and function name set in the LogRecord created for the logging event. This can be used in logging helpers so that the function name, filename and line number recorded are not the information for the helper function/method, but rather its caller. The name of this parameter mirrors the equivalent one in the warnings module

I hope this will be helpful for future researchers.

like image 122
Aleksandr Avatar answered Sep 15 '25 03:09

Aleksandr