在Python中使用funcName进行日志记录会影响性能吗?
我刚刚在看Python的日志文档,发现了一个叫funcName的参数,它在日志格式化的时候会用到。
这个功能看起来很方便,能清楚地看到日志是从哪里来的,但有人提出了一个担忧,可能是因为它需要生成一个堆栈跟踪,这样会影响性能。
我猜它可能使用了类似sys._getframe()的东西,而不是inspect模块,因为后者会对性能有影响。
那么,funcName这个功能在生产环境中可以用吗,还是说我们应该避免使用它呢?
2 个回答
1
这里有一个测试应用,它展示了在我的本地机器上,写入文件名和行号到一个文件大约需要1秒钟来处理500000个请求。
#!/usr/bin/env python
import traceback, sys, time
def writeinfo(f, on=True):
# give the function something to do
s=sum(range(1000))
if on:
fr = sys._getframe(1)
s = "%s (line %s) " % (fr.f_code.co_filename, fr.f_lineno)
f.write(s)
cnt = 50000
t1 = time.time()
f = open('tempfile.log','w')
for i in range(cnt):
writeinfo(f)
f.close()
t2 = time.time()
for i in range(cnt):
writeinfo(f, on=False)
t3 = time.time()
print "Test time with file write: %s" % (t2-t1)
print "Test time without file write: %s" % (t3-t2)
结果:
Test time with file write: 1.17307782173
Test time without file write: 1.08166718483
4
别急着猜,Python自带的日志记录功能的源代码你是可以找到的。
它是怎么找到函数名字的(在logging/__init__.py
文件里):
#
# _srcfile is used when walking the stack to check when we've got the first
# caller stack frame.
#
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)
# next bit filched from 1.5.2's inspect.py
def currentframe():
"""Return the frame object for the caller's stack frame."""
try:
raise Exception
except:
return sys.exc_info()[2].tb_frame.f_back
if hasattr(sys, '_getframe'): currentframe = lambda: sys._getframe(3)
# done filching
然后后面会有:
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 == _srcfile:
f = f.f_back
continue
rv = (filename, f.f_lineno, co.co_name)
break
return rv
另外,也不用担心性能问题:它会在判断你是否需要这个功能之前就找到函数名字,所以你可以放心使用。