logging module - how to include method's class name when using %(funcName)
Peter Otten
__peter__ at web.de
Tue Sep 11 13:46:27 EDT 2018
Malcolm Greene wrote:
> I'm using the Python logging module and looking for a way to include a
> method's class name when using %(funcName). Is this possible? When you
> have several related classes, just getting the function (method) name is
> not enough information to provide context on the code being executed.
> I'm outputting module name and line number so I can always go back and
> double check a caller's location in source, but that seems like an
> archaic way to find this type of information.
In the code below I took the same approach, but automated it with pyclbr.
While I'm not really happy with the result (is inspect or ast more suitable,
should Logger be subclassed or adapted rather than specifying the LogRecord
factory, can the class be determined lazily) here's the current state of
things:
$ cat findclass.py
def ham(): log.warning("inside ham function")
import bisect
import pyclbr
import logging
LogRecord = logging.LogRecord
class Module:
def __init__(self, module):
mod = pyclbr.readmodule_ex(module)
line2func = []
for classname, cls in mod.items():
if isinstance(cls, pyclbr.Function):
line2func.append((cls.lineno, "<no-class>", cls.name))
else:
for methodname, start in cls.methods.items():
line2func.append((start, classname, methodname))
line2func.sort()
keys = [item[0] for item in line2func]
self.line2func = line2func
self.keys = keys
def line_to_class(self, lineno):
index = bisect.bisect(self.keys, lineno) - 1
return self.line2func[index][1]
def lookup_module(module):
return Module(module)
def lookup_class(module, funcname, lineno):
if funcname == "<module>":
return "<no-class>"
module = lookup_module(module)
return module.line_to_class(lineno)
def makeLogRecord(*args, **kw):
record = LogRecord(*args, **kw)
record.className = lookup_class(
record.module, record.funcName, record.lineno
)
if record.className != "<no-class>":
record.funcName = "{}.{}".format(record.className, record.funcName)
return record
logging.setLogRecordFactory(makeLogRecord)
logging.basicConfig(
format=logging.BASIC_FORMAT
+ " class=%(className)s func=%(funcName)s lineno=%(lineno)s"
)
log = logging.getLogger()
log.warning("module-level")
def foo():
log.warning("inside foo function")
def bar(): log.warning("inside bar function")
class A:
def foo(self):
log.warning("inside A.foo method")
class B:
def foo(self):
log.warning("inside B.foo method")
A().foo()
B().foo()
foo()
bar()
ham()
$ python3.7 findclass.py
WARNING:root:module-level class=<no-class> func=<module> lineno=61
WARNING:root:inside A.foo method class=A func=A.foo lineno=71
WARNING:root:inside B.foo method class=B func=B.foo lineno=76
WARNING:root:inside foo function class=<no-class> func=foo lineno=65
WARNING:root:inside bar function class=<no-class> func=bar lineno=66
WARNING:root:inside ham function class=<no-class> func=ham lineno=1
More information about the Python-list
mailing list