I am trying to build a decorator that allows me to log functions in my classes in Python3. The decorator is as follows:
import functools
def log(_func=None, *, logger):
def decorator_log(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
try:
logger.info(f"Entering function {func.__qualname__}")
args_repr = [repr(a) for a in args]
kwargs_repr = [f"{k}={v!r}" for k, v in kwargs.items()]
signature = ", ".join(args_repr + kwargs_repr)
logger.debug(f"function {func.__qualname__} called with args {signature}")
try:
result = func(*args, **kwargs)
return result
except Exception as e:
logger.exception(f"Exception raised in {func.__qualname__}. exception: {str(e)}")
raise e
logger.info(f"Leaving function {func.__qualname__}.{func.__name__}")
except Exception:
pass
return wrapper
if _func is None:
return decorator_log
else:
return decorator_log(_func)
This is then used to decorate class functions like so:
class MainController(BaseController):
logger = logging.getLogger(__name__)
def __init__(self, main_model, main_view):
...
@log(logger=logger)
def initialize_components(self):
...
etc.
This works fine, but runs into issues when I use base classes and subclasses. In particular, I want to decorate almost all functions in both the base class and the subclass, but when I do this, func.__qualname__
will be the subclass name only when the subclass overrides a base class method, and it will be the base class name otherwise.
How can I modify this approach so that even in cases where the subclass does not override the base class method, a decorator applied to the base class method will print the name of the subclass from which it was called instead of the base class?