25

我有如下的日志记录功能。

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']
4

8 回答 8

20

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

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

大多数人在记录异常时只需要堆栈,如果您调用logging.exception(). 如果您真的需要其他时间的堆栈信息,那么您将需要使用 traceback 模块来提取您需要的其他信息。

于 2011-02-23T16:01:06.380 回答
19
import inspect
import traceback

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

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

于 2011-02-23T15:47:33.027 回答
12

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

于 2011-02-23T22:40:08.927 回答
4

迟到的答案,但哦,好吧。

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

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 日志包本身的堆栈帧。(它也可能需要针对不同版本的日志包进行调整)

于 2014-10-31T19:33:39.623 回答
3

使用回溯模块。

logging.error(traceback.format_exc())
于 2011-02-23T15:46:44.773 回答
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()
于 2011-02-23T16:06:33.290 回答
1

这是基于@mouad的回答,但通过在每个级别包括文件名(但不是其完整路径)和调用堆栈的行号,并将堆栈留在最近调用的来自(即不颠倒)顺序,因为这是我想阅读它的方式:-)

每个条目都有 file:line:func() ,它与普通堆栈跟踪的序列相同,但都在同一行上更加紧凑。

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()

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

logger.debug("\nWIRE: justdoit request -----\n"+callers()+"\n\n")
于 2019-03-11T17:18:35.487 回答
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
于 2011-02-23T15:50:24.367 回答