正如马丁(Martijn)指出的那样,事情有时会改变。但是,由于您使用的是Python 2(迭代器已将其放弃),因此如果您不介意猴子修补日志记录,则以下代码将起作用:
from functools import wraps
import logging
class ArgLogger(object):
"""
Singleton class -- will only be instantiated once
because of the monkey-patching of logger.
"""
singleton = None
def __new__(cls):
self = cls.singleton
if self is not None:
return self
self = cls.singleton = super(ArgLogger, cls).__new__(cls)
self.code_location = None
# Do the monkey patch exactly one time
def findCaller(log_self):
self.code_location, code_location = None, self.code_location
if code_location is not None:
return code_location
return old_findCaller(log_self)
old_findCaller = logging.Logger.findCaller
logging.Logger.findCaller = findCaller
return self
def log_args(self, logger, level=logging.DEBUG):
"""Decorator to log arguments passed to func."""
def inner_func(func):
co = func.__code__
code_loc = (co.co_filename, co.co_firstlineno, co.co_name)
@wraps(func)
def return_func(*args, **kwargs):
arg_list = list("{!r}".format(arg) for arg in args)
arg_list.extend("{}={!r}".format(key, val)
for key, val in kwargs.iteritems())
msg = "{name}({arg_str})".format(name=func.__name__,
arg_str=", ".join(arg_list))
self.code_location = code_loc
logger.log(level, msg)
return func(*args, **kwargs)
return return_func
return inner_func
log_args = ArgLogger().log_args
if __name__ == "__main__":
logger = logging.getLogger(__name__)
handler = logging.StreamHandler()
fmt = "%(asctime)s %(levelname)-8.8s [%(name)s:%(lineno)4s] %(message)s"
handler.setFormatter(logging.Formatter(fmt))
logger.addHandler(handler)
logger.setLevel(logging.DEBUG)
@log_args(logger)
def foo(x, y, z):
pass
class Bar(object):
@log_args(logger)
def baz(self, a, b, c):
pass
def test_regular_log():
logger.debug("Logging without ArgLog still works fine")
foo(1, 2, z=3)
foo(1, 2, 3)
foo(x=1, y=2, z=3)
bar = Bar()
bar.baz(1, c=3, b=2)
test_regular_log()