Today I was challenged to build atop Python's logging module to automatically log the filepath from whence the log statement was issued.
The guidance in the https://docs.python.org/3/howto/logging.html#advanced-logging-tutorial is to logger = logging.getLogger(__name__) atop each module.
The question was: "Can I clean the callsite?"
So, for example:
# Foo.py
from LoggingHandler import log
class Foo:
def __init__(self):
log.debug('🔹Foo Debug')
log.info('🔹Foo info')
# Bar.py
from LoggingHandler import log
class Bar:
def __init__(self):
log.debug('🔹Bar Debug')
log.info('🔹Bar info')
# main.py
from Foo import Foo
from Bar import Bar
if __name__ == '__main__':
foo = Foo()
bar = Bar()
... would output:
2022-09-18 13:36:04,059-DEBUG-/path/to/Foo.py-🔹Foo Debug
2022-09-18 13:36:04,060-INFO-/path/to/Foo.py-🔹Foo info
2022-09-18 13:36:04,061-DEBUG-/path/to/Bar.py-🔹Bar Debug
2022-09-18 13:36:04,061-INFO-/path/to/Bar.py-🔹Bar info
This is what I came up with:
# LoggingHandler.py
import logging, inspect
from pathlib import Path
loggers = {}
def fetchOrCreateLogger():
if (filepath := inspect.stack()[2].filename) not in loggers.keys():
loggr = logging.getLogger(filepath)
loggr.setLevel(logging.DEBUG)
fh = logging.FileHandler(f'./{Path(filepath).stem}.txt')
ch = logging.StreamHandler()
fh.setLevel(logging.DEBUG)
ch.setLevel(logging.DEBUG)
formatter = logging.Formatter('%(asctime)s-%(levelname)s-%(name)s-%(message)s')
fh.setFormatter(formatter)
ch.setFormatter(formatter)
loggr.addHandler(fh)
loggr.addHandler(ch)
loggers[filepath] = loggr
return loggers[filepath]
class Bouncer:
def __getattr__(self, item):
return getattr(fetchOrCreateLogger(), item)
log = Bouncer()
I'm not sure whether I like it or not. I have little experience with this logging module. I would be grateful for insight from a better vantage point.