[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

logging module - how to include method's class name when using %(funcName)

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 

$ cat
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>",
                for methodname, start in cls.methods.items():
                    line2func.append((start, classname, methodname))

        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


    + " class=%(className)s func=%(funcName)s lineno=%(lineno)s"

log = logging.getLogger()

def foo():
    log.warning("inside foo function")
def bar(): log.warning("inside bar function")

class A:
    def foo(self):
        log.warning("inside method")

class B:
    def foo(self):
        log.warning("inside method")
$ python3.7
WARNING:root:module-level class=<no-class> func=<module> lineno=61
WARNING:root:inside method class=A lineno=71
WARNING:root:inside method class=B 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