This is the formatting string that I am using for logging:
\'%(asctime)s - %(levelname)-10s - %(funcName)s - %(message)s\'
But to show the logg
Thanks to @cygnusb and the others who already provided useful pointers. I chose to use the Python 3.4 Logger.findCaller method as my starting point. The following solution has been tested with Python 2.7.9 and 3.4.2. This code is meant to be placed in its own module. It produces the correct answer with only one iteration of the loop.
import io
import sys
def _DummyFn(*args, **kwargs):
"""Placeholder function.
Raises:
NotImplementedError
"""
_, _ = args, kwargs
raise NotImplementedError()
# _srcfile is used when walking the stack to check when we've got the first
# caller stack frame, by skipping frames whose filename is that of this
# module's source. It therefore should contain the filename of this module's
# source file.
_srcfile = os.path.normcase(_DummyFn.__code__.co_filename)
if hasattr(sys, '_getframe'):
def currentframe():
return sys._getframe(3)
else: # pragma: no cover
def currentframe():
"""Return the frame object for the caller's stack frame."""
try:
raise Exception
except Exception:
return sys.exc_info()[2].tb_frame.f_back
class WrappedLogger(logging.Logger):
"""Report context of the caller of the function that issues a logging call.
That is, if
A() -> B() -> logging.info()
Then references to "%(funcName)s", for example, will use A's context
rather than B's context.
Usage:
logging.setLoggerClass(WrappedLogger)
wrapped_logging = logging.getLogger("wrapped_logging")
"""
def findCaller(self, stack_info=False):
"""Return the context of the caller's parent.
Find the stack frame of the caller so that we can note the source
file name, line number and function name.
This is based on the standard python 3.4 Logger.findCaller method.
"""
sinfo = None
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
if sys.version_info.major == 2:
rv = "(unknown file)", 0, "(unknown function)"
else:
rv = "(unknown file)", 0, "(unknown function)", sinfo
while hasattr(f, "f_code"):
co = f.f_code
filename = os.path.normcase(co.co_filename)
if filename == _srcfile or filename == logging._srcfile:
f = f.f_back
continue
# We want the frame of the caller of the wrapped logging function.
# So jump back one more frame.
f = f.f_back
co = f.f_code
if sys.version_info.major == 2:
rv = "(unknown file)", 0, "(unknown function)"
else:
rv = "(unknown file)", 0, "(unknown function)", sinfo
while hasattr(f, "f_code"):
co = f.f_code
filename = os.path.normcase(co.co_filename)
if filename == _srcfile or filename == logging._srcfile:
f = f.f_back
continue
# We want the frame of the caller of the wrapped logging function.
# So jump back one more frame.
f = f.f_back
co = f.f_code
if sys.version_info.major == 2:
rv = co.co_filename, f.f_lineno, co.co_name
else:
if stack_info:
sio = io.StringIO()
sio.write('Stack (most recent call last):\n')
traceback.print_stack(f, file=sio)
sinfo = sio.getvalue()
if sinfo[-1] == '\n':
sinfo = sinfo[:-1]
sio.close()
rv = co.co_filename, f.f_lineno, co.co_name, sinfo
break
return rv
As suggested in the first answer, subclassing the Logger class and using logging.setLoggerClass should do the trick. You will need a modified findCaller function, that is handling your wrapped function call(s).
Put the following into a module, since the findCaller class method is searching the first call from a file, which is not the current source filename.
import inspect
import logging
import os
if hasattr(sys, 'frozen'): #support for py2exe
_srcfile = "logging%s__init__%s" % (os.sep, __file__[-4:])
elif __file__[-4:].lower() in ['.pyc', '.pyo']:
_srcfile = __file__[:-4] + '.py'
else:
_srcfile = __file__
_srcfile = os.path.normcase(_srcfile)
class WrappedLogger(logging.Logger):
def __init__(self,name):
logging.Logger.__init__(self, name)
def findCaller(self):
"""
Find the stack frame of the caller so that we can note the source
file name, line number and function name.
"""
# get all outer frames starting from the current frame
outer = inspect.getouterframes(inspect.currentframe())
# reverse the order, to search from out inward
outer.reverse()
rv = "(unknown file)", 0, "(unknown function)"
pos = 0
# go through all frames
for i in range(0,len(outer)):
# stop if we find the current source filename
if outer[i][1] == _srcfile:
# the caller is the previous one
pos=i-1
break
# get the frame (stored in first tuple entry)
f = outer[pos][0]
co = f.f_code
rv = (co.co_filename, f.f_lineno, co.co_name)
return rv
# Usage:
logging.setLoggerClass(WrappedLogger)
log = logging.getLogger("something")