logging: local functions ==> loss of lineno

Peter Otten __peter__ at web.de
Fri Mar 19 08:24:41 EDT 2010


Jean-Michel Pichavant wrote:

> Hellmut Weber wrote:
>> Am 11.03.2010 12:14, schrieb Peter Otten:
>>> Hellmut Weber wrote:
>>>
>>>> Logging works very well giving the filename and line number of the
>>>> point
>>>> where it is called. As long as I use the loggers directly.
>>>> BUT when I have to wrap the logger call in some other function, I
>>>> always
>>>> get file name and line number of the call of the logger inside the
>>>> wrapping function.
>>>>
>>>> Is there a possibility to get this information in this situation too?
>>>
>>> The official way is probably to write a custom Logger class that
>>> overrides
>>> the findCaller() method.
>>>
>>> Below is a hack that monkey-patches the logging._srcfile attribute to
>>> ignore
>>> user-specified modules in the call stack:
>>>
>>> $ cat wrapper.py
>>> import logging
>>> import os
>>> import sys
>>>
>>> logger = logging.getLogger()
>>>
>>> class SrcFile(object):
>>>      def __init__(self, exclude_files):
>>>          self.files = set(exclude_files)
>>>      def __eq__(self, other):
>>>          return other in self.files
>>>
>>> def fixname(filename):
>>>      if filename.lower().endswith((".pyc", ".pyo")):
>>>          filename = filename[:-4] + ".py"
>>>      return os.path.normcase(filename)
>>>
>>> if "--monkey" in sys.argv:
>>>      print "patching"
>>>      logging._srcfile = SrcFile([logging._srcfile, fixname(__file__)])
>>>
>>> def warn(*args, **kw):
>>>      logger.warn(*args, **kw)
>>>
>>> $ cat main.py
>>> import logging
>>> logging.basicConfig(format="%(filename)s<%(lineno)s>: %(message)s")
>>> import wrapper
>>> wrapper.warn("foo")
>>> wrapper.warn("bar")
>>> wrapper.warn("baz")
>>>
>>> $ python main.py
>>> wrapper.py<23>: foo
>>> wrapper.py<23>: bar
>>> wrapper.py<23>: baz
>>>
>>> $ python main.py --monkey
>>> patching
>>> main.py<4>: foo
>>> main.py<5>: bar
>>> main.py<6>: baz
>>>
>>> $ python -V
>>> Python 2.6.4
>>>
>>> Peter
>>
>> Hi Peter,
>> your hack is exactly what I was looking for.
>> It permits to configure my logging messages as I want, e.g. using
>> different colors for different classes of messages.
>>
>> I do not yet understand all details WHY it is working but suppose some
>> study of the logging module will help me to understand.
>>
>> Thank you very much
>>
> _scrFile is a private attribute of the logging module. Don't change it.
> 
> As you said yourself, 'The official way is probably to write a custom
> Logger class that overrides
> the findCaller() method'

OK, I tried the this approach, too:

import logging
import os
import sys

from logging import currentframe

def fixname(filename):
    if filename.lower().endswith((".pyc", ".pyo")):
        filename = filename[:-4] + ".py"
    return os.path.normcase(filename)

class MyLogger(logging.Logger):
    exclude_files = set([logging._srcfile, fixname(__file__)])

    def findCaller(self):
        """
        Find the stack frame of the caller so that we can note the source
        file name, line number and function name.
        """
        f = currentframe()
        #On some versions of IronPython, currentframe() returns None if
        #IronPython isn't run with -X:Frames.
        if f is not None:
            f = f.f_back
        rv = "(unknown file)", 0, "(unknown function)"
        while hasattr(f, "f_code"):
            co = f.f_code
            filename = os.path.normcase(co.co_filename)
            if filename in self.exclude_files:
                f = f.f_back
                continue
            rv = (filename, f.f_lineno, co.co_name)
            break
        return rv

if "--custom-logger" in sys.argv:
    print "setting custom logger"
    logging.setLoggerClass(MyLogger)
    logging.root = MyLogger("root", logging.WARNING)

logger = logging.getLogger()
def warn(*args, **kw):
    logger.warn(*args, **kw)

I had to duplicate the original findCaller() method with only one line 
changed. This means I have now some code duplication and I still have to 
monitor future changes in the logging source.

In this case the "official way" seems to be more intrusive than the "hack".

Peter



More information about the Python-list mailing list