所以我在Python中有一个有趣的问题,可以通过面向方面的技术来解决。情况如下:
有没有方便的方法在Python中执行此操作,而不会在每个模块的函数中粘贴日志语句?
答案 0 :(得分:5)
可以使用装饰器修改可执行文件中函数的行为:
#!/usr/bin/env python
from module1 import foo
from module2 import bar
def trace(f):
def tracewrapper(*arg, **kw):
arg_str=','.join(['%r'%a for a in arg]+['%s=%s'%(key,kw[key]) for key in kw])
print "%s(%s)" % (f.__name__, arg_str)
return f(*arg, **kw)
return tracewrapper
verbose_functions=[foo,bar] # add whatever functions you want logged here
for func in verbose_functions:
globals()[func.func_name]=trace(func)
由于您只修改了可执行文件命名空间中函数的定义,因此模块的功能保持不变。当一个模块的函数调用另一个模块的函数时,它会被跟踪修改,并且不会生成日志语句。
如果你想直接从main()来记录函数调用, 那么你可以使用这样的跟踪装饰器:
import traceback
def trace(f,filename,funcname):
def tracewrapper(*arg, **kw):
stacks=traceback.extract_stack()
(s_filename,s_lineno,s_funcname,s_text)=stacks[-2]
# Alternatively, you can search the entire call stack
# for (s_filename,s_lineno,s_funcname,s_text) in stacks:
if s_filename.endswith(filename) and s_funcname==funcname:
arg_str=','.join(['%r'%a for a in arg]+
['%s=%s'%(key,kw[key]) for key in kw])
print "%s(%s)" % (f.__name__, arg_str)
return f(*arg, **kw)
return tracewrapper
verbose_functions=[foo,bar] # add whatever functions you want logged here
for func in verbose_functions:
# You can pass the module's filename and the function name here
globals()[func.func_name]=trace(func,'test.py','main')
请注意,使用上述跟踪
def baz():
foo(3,4)
def main():
foo(1,2,'Hi')
bar(x=3)
baz()
会记录foo(1,2,'Hi')
和bar(x=3)
来电,但是不是 foo(3,4)
来电不是来自直接来自。但是,它主要来自main,因为主要调用baz
。如果你想记录foo(3,4)
电话,那么你想要
遍历整个堆栈:
import traceback
def trace(f,filename,funcname):
def tracewrapper(*arg, **kw):
stacks=traceback.extract_stack()
for (s_filename,s_lineno,s_funcname,s_text) in stacks:
if s_filename.endswith(filename) and s_funcname==funcname:
arg_str=','.join(['%r'%a for a in arg]+
['%s=%s'%(key,kw[key]) for key in kw])
print "%s(%s)" % (f.__name__, arg_str)
return f(*arg, **kw)
return tracewrapper
答案 1 :(得分:0)
最简单的第一个解决方案是使用代理模块。
#fooproxy.py
import foo
import logger #not implemented here - use your imagination :)
def bar(baz):
logger.method("foo.bar")
return foo.bar(baz)
#foo.py
def bar(baz):
print "The real McCoy"
#main.py
import fooproxy as foo
foo.bar()
答案 2 :(得分:0)
我不知道在一般情况下这样做的正确方法,但我想到的只是稍微破解可执行文件。类似的东西:
class DebugCallWrapper(object):
def __init__(self, callable):
self.callable = callable
def __call__(*args,**kwargs):
log.debug(str(callable) + str(args) + str(kwargs))
callable(*args,**kwargs)
class Top(object):
def __getattribute__(self, name):
real_object = globals()[name]
if callable(real_object):
return DebugCallWrapper(real_object)
else:
return real_object
top = Top()
import foo
#instead of foo.bar()
top.foo.bar()
这种事情可能会给你带来很多麻烦,如果没有一些调整,上述内容可能无法使用。但也许这是一个想法。