Python日志库如何高效获取行号和函数名?

1 投票
2 回答
55 浏览
提问于 2025-04-14 15:59

在我的Python服务中,我使用的是一个内部开发的日志库,这个库和Python自带的日志模块不兼容。在使用这个库的时候,我想在日志中添加一些额外的信息,比如行号和函数名。我一直在用inspect库来获取这些信息,像下面这样:

frame = inspect.currentframe().f_back
frame_info = inspect.getframeinfo(frame)
frame_info.lineno

不过根据在Python inspect.stack很慢的讨论,使用inspect库的方法有点耗费资源,因为它们需要访问文件系统。我们注意到因为这个原因,我们的服务性能下降了(虽然比起之前使用inspect.stack()[0]时的情况要好很多)。

我发现Python自带的日志模块能够有效地获取函数名和行号。这些信息通过LogRecord属性暴露出来:https://python.readthedocs.io/en/latest/library/logging.html#logrecord-attributes

我的问题是,Python的日志模块是怎么做到高效收集这些信息的?我试着去看它的源代码,但很难找到合适的地方。

2 个回答

3

为了避免加载 .py 文件时变慢,可以这样获取信息:

import traceback

for frame, _lineno in traceback.walk_stack(None):
  print(frame.f_code.co_name, frame.f_lineno)
  break

如果你查看 traceback.walk_stack 的实现(在 Lib/traceback.py 文件中),你会发现其实可以更简单:

import sys

frame = sys._getframe().f_back
if frame is not None:
  print(frame.f_code.co_name, frame.f_lineno)

想了解更多信息,可以查看标准库中 traceback 模块的文档。

2

你不需要完整调用 inspect.getframeinfo。你可以直接获取帧对象的 f_lineno 和它的代码对象的 co_name,这样做比使用 inspect.getframeinfo 省事多了。

lineno = frame.f_lineno
funcname = frame.f_code.co_name

根据我的测试,这种方法比使用 inspect.getframeinfo 快大约100倍。

撰写回答