Inspecting the stack on every logger call
Although fetchOrCreateLogger caches the loggers to avoid creating them repeatedly,
inspect.stack() is called for every logger method call (every log.debug, log.info, and so on).
Note that inspecting the stack is usually not cheap.
You could replace log = Bouncer() with a factory method:
# in LoggingHandler
def callsite_path_logger():
filepath = inspect.stack()[1].filename
# ...
return logger
And then use it in files that want this kind of logger as:
from LoggingHandler import callsite_path_logger
log = callsite_path_logger()
Notice that with this approach you don't need the cache anymore.
One caveat is that you must remember to not pass this logger to code in other files, because the logging calls will use filepath that was determined at construction time.
If you want a logger that will always report the correct path no matter from where it is called, then indeed you must inspect the call stack non every logging call, as in the original code. I don't recall such use case in practice, most of time loggers are created per file, using a factory method.
Transparent caching
The posted code manages the cache of loggers.
You could benefit from functools.cache to do that for you,
getting rid of the loggers cache:
@functools.cache
def logger_for(filepath):
logger = logging.getLogger(filepath)
# ...
return logger
class Bouncer:
def __getattr__(self, item):
return getattr(logger_for(inspect.stack()[1].filename), item)
Minor issues
Instead of path in loggers.keys() you can write simply path in loggers.
Instead of this:
if (filepath := inspect.stack()[2].filename) not in loggers:
I think this is easier to read, with one statement per line:
filepath = inspect.stack()[2].filename
if filepath not in loggers:
I would rename loggr to the natural spelling logger.