Let there be a class with functions:
class Tester:
@Logger()
def __init__(self):
print(__class__)
@Logger()
def func(self, num):
return num**2
where Logger is a decorator roughly defined as:
from typing import Optional, Any
from logging import getLogger
class Logger:
def __init__(self):
self.logger = getLogger()
self.logging_function = getattr(self, 'function')
def __call__(self, decorator: callable):
def f(*args, **kwargs):
return self.logging_function(decorator, *args, **kwargs)
return f
def function(self, func: callable, *args: Optional[Any], **kwargs: Optional[Any]):
func_name = Logger.get_name(func)
self.logger.info(f"Starting: {func_name}.")
return func(*args, **kwargs)
@staticmethod
def get_name(func):
return f'__init__ {func.__class__.__name__}' if func.__name__ == '__init__' else func.__name__
How can we edit the Logger get_name
function, such that if the function being run is a class __init__
that the name returned is __init__ Tester
, but if the function is named something else it merely returns the function __name__
?
(AKA) Expected output:
>>> test = Tester()
INFO: Starting __init__ Tester.
<class '__main__.Tester'>
>>> test.func(3)
INFO: Starting func.
9
Current Output:
>>> test = Tester()
INFO: Starting __init__ function.
<class '__main__.Tester'>
>>> test.func(3)
INFO: Starting func.
9
You could use the qualified name instead like this:
@staticmethod
def get_name(func):
return func.__qualname__
Which will give you something like:
>>> test = Tester()
INFO:Starting: Tester.__init__.
<class '__main__.Tester'>
>>> test.func(3)
INFO:Starting: Tester.func.
9
You might also be interested in the standard LogRecord
attribute funcName
, which does a similar thing from within the function. A basic demo of that:
import logging
logging.basicConfig(
level=logging.INFO,
format="%(levelname)s (%(funcName)s) %(message)s",
)
log = logging.getLogger()
class A:
def my_method(self):
log.info("hello")
def bar():
log.info("world")
A().my_method()
bar()
Outputs this:
INFO (my_method) hello
INFO (bar) world