【问题标题】:How can I log current line, and stack info with Python?如何使用 Python 记录当前行和堆栈信息?
【发布时间】:2011-02-23 15:34:53
【问题描述】:

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

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

【问题讨论】:

    标签: python logging traceback


    【解决方案1】:

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

    【讨论】:

    • 文档对此有点罗嗦。我错过了,谢谢!
    【解决方案2】:
    import inspect
    import traceback
    
    def method():
       frame = inspect.currentframe()
       stack_trace = traceback.format_stack(frame)
       print ''.join(stack_trace)
    

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

    【讨论】:

    • 而不是做stack_trace[:-1](这意味着它需要格式化比你使用的多一帧,然后对结果进行切片),你不能这样做:frame = inspect.currentframe(1) 这样你就可以在没有顶层,所以format_stack不需要处理,从format_stack返回不需要处理?
    【解决方案3】:

    迟到的答案,但是很好。

    另一个解决方案是您可以使用文档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】:

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

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

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

      【讨论】:

      【解决方案5】:

      使用traceback 模块。

      logging.error(traceback.format_exc())
      

      【讨论】:

        【解决方案6】:

        举个例子,希望对你有帮助:

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

        【讨论】:

          【解决方案7】:

          这基于@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()
          

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

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

          【讨论】:

            【解决方案8】:

            查看回溯模块

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

            【讨论】:

              猜你喜欢
              • 2013-10-02
              • 1970-01-01
              • 2019-07-11
              • 2015-05-29
              • 2021-02-07
              • 2010-10-10
              • 2012-12-27
              • 2020-02-09
              • 2011-04-06
              相关资源
              最近更新 更多