如何使用Python记录当前行和堆栈信息?

时间:2011-02-23 15:34:54

标签: python logging traceback

我的记录功能如下。

logging.basicConfig(
    filename = fileName,
    format = "%(levelname) -10s %(asctime)s %(message)s",
    level = logging.DEBUG
)

def printinfo(string):
    if DEBUG:
        logging.info(string)

def printerror(string):
    if DEBUG:
        logging.error(string)
    print string

我需要登录行号,堆栈信息。例如:

1: def hello():
2:    goodbye()
3:
4: def goodbye():
5:    printinfo()

---> Line 5: goodbye()/hello()

我怎样才能用Python做到这一点?

解决

def printinfo(string):
    if DEBUG:
        frame = inspect.currentframe()
        stack_trace = traceback.format_stack(frame)
        logging.debug(stack_trace[:-1])
    if LOG:
        logging.info(string)

给了我这个信息,这正是我所需要的。

DEBUG      2011-02-23 10:09:13,500 [
  '  File "/abc.py", line 553, in <module>\n    runUnitTest(COVERAGE, PROFILE)\n', 
  '  File "/abc.py", line 411, in runUnitTest\n    printinfo(string)\n']

8 个答案:

答案 0 :(得分:18)

您只需更改格式字符串以包含它们即可执行当前功能名称,模块和行号。

logging.basicConfig(
    filename = fileName,
    format = "%(levelname) -10s %(asctime)s %(module)s:%(lineno)s %(funcName)s %(message)s",
    level = logging.DEBUG
)

大多数人在记录异常时只需要堆栈,而如果您调用logging.exception(),则日志记录模块会自动执行此操作。如果您真的想要其他时间的堆栈信息,那么您将需要使用回溯模块来提取所需的其他信息。

答案 1 :(得分:17)

import inspect
import traceback

def method():
   frame = inspect.currentframe()
   stack_trace = traceback.format_stack(frame)
   print ''.join(stack_trace)

使用stack_trace [: - 1]避免在堆栈跟踪中包含方法/ printinfo。

答案 2 :(得分:9)

从Python 3.2开始,这可以简化为将stack_info=True标志传递给logging calls。但是,对于任何早期版本,您都需要使用上述答案之一。

答案 3 :(得分:4)

迟到的回答,但是很好。

另一种解决方案是,您可以使用文档here中指定的过滤器创建自己的格式化程序。这是一个非常棒的功能,因为您现在不再需要使用辅助函数(并且必须将帮助函数放在您想要堆栈跟踪的任何位置)。相反,自定义格式化将其直接实现到日志中。

import logging
class ContextFilter(logging.Filter):
    def __init__(self, trim_amount)
        self.trim_amount = trim_amount
    def filter(self, record):
        import traceback
        record.stack = ''.join(
            str(row) for row in traceback.format_stack()[:-self.trim_amount]
        )
        return True

# Now you can create the logger and apply the filter.
logger = logging.getLogger(__name__)
logger.addFilter(ContextFilter(5))

# And then you can directly implement a stack trace in the formatter.    
formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s \n %(stack)s')

注意:在上面的代码中,我修剪了最后5个堆栈帧。这只是为了方便,因此我们不会显示python日志包本身的堆栈帧。(也可能需要针对不同版本的日志包进行调整)

答案 4 :(得分:3)

以下是我希望它可以为您提供帮助的示例:

import inspect
import logging

logging.basicConfig(
    format = "%(levelname) -10s %(asctime)s %(message)s",
    level = logging.DEBUG
)

def test():

    caller_list = []
    frame = inspect.currentframe()
    this_frame = frame  # Save current frame.

    while frame.f_back:
        caller_list.append('{0}()'.format(frame.f_code.co_name))
        frame = frame.f_back

    caller_line = this_frame.f_back.f_lineno
    callers =  '/'.join(reversed(caller_list))

    logging.info('Line {0} : {1}'.format(caller_line, callers))

def foo():
    test()

def bar():
    foo()

bar()

结果:

INFO       2011-02-23 17:03:26,426 Line 28 : bar()/foo()/test()

答案 5 :(得分:1)

使用traceback模块。

logging.error(traceback.format_exc())

答案 6 :(得分:1)

这是基于@mouad的答案,但通过在每个级别都包括调用堆栈的文件名(但不包括其完整路径)和行号,并且将堆栈保留在最近的位置而变得更加有用(IMO) -from(即不可逆)顺序,因为那是我要阅读的方式:-)

每个条目都具有file:line:func(),其顺序与普通stacktrace相同,但都在同一行上,因此更加紧凑。

import inspect

def callers(self):
    caller_list = []
    frame = inspect.currentframe()
    while frame.f_back:
        caller_list.append('{2}:{1}:{0}()'.format(frame.f_code.co_name,frame.f_lineno,frame.f_code.co_filename.split("\\")[-1]))
        frame = frame.f_back
    callers =  ' <= '.join(caller_list)
    return callers

如果您有任何中间调用来生成日志文本,则可能需要添加额外的f_back。

        frame = inspect.currentframe().f_back

产生这样的输出:

file2.py:620:func1() <= file3.py:211:func2() <= file3.py:201:func3() <= main.py:795:func4() <= file4.py:295:run() <= main.py:881:main()

我只需要两个关键函数中的这个stacktrace,因此我将调用者的输出添加到logger.debug()调用的文本中,例如htis:

logger.debug("\nWIRE: justdoit request -----\n"+callers()+"\n\n")

答案 7 :(得分:0)

查看追溯模块

>>> import traceback
>>> def test():
>>>     print "/".join( str(x[2]) for x in traceback.extract_stack() )
>>> def main():
>>>     test()
>>> main()
<module>/launch_new_instance/mainloop/mainloop/interact/push/runsource/runcode/<module>/main/test