【问题标题】:How do I can format exception stacktraces in Python logging?如何在 Python 日志记录中格式化异常堆栈跟踪?
【发布时间】:2015-01-27 21:16:39
【问题描述】:

我在 Python 中创建的日志旨在临时存储为文件,然后这些文件将被处理到日志数据库中。它们采用管道描述的格式来指示如何处理日志,但是 logging.exception() 添加了太多的字段和太多的换行符,从而打破了我的标准。

import logging
logging.basicConfig(filename='output.txt', 
                    format='%(asctime)s|%(levelname)s|%(message)s|', 
                    datefmt='%m/%d/%Y %I:%M:%S %p', 
                    level=logging.DEBUG)
logging.info('Sample message')

try:
    x = 1 / 0
except ZeroDivisionError as e:
    logging.exception('ZeroDivisionError: {0}'.format(e))

# output.txt
01/27/2015 02:09:01 PM|INFO|Sample message|
01/27/2015 02:09:01 PM|ERROR|ZeroDivisionError: integer division or modulo by zero|
Traceback (most recent call last):
  File "C:\Users\matr06586\Desktop\ETLstage\Python\blahblah.py", line 90, in <module>
    x = 1 / 0
ZeroDivisionError: integer division or modulo by zero

如何最好地处理或格式化带有空格和换行符的回溯?这些消息是 logging.exception() 的重要组成部分,但是当我尝试记录实例时绕过该函数感觉很奇怪的例外。如何记录我的回溯并格式化它们?是否应该忽略回溯?

感谢您的宝贵时间!

【问题讨论】:

  • 你是在问你应该做什么,或者怎么做?您希望如何格式化日志文件中的错误消息取决于您。 希望他们看起来像什么?
  • 感谢您解决这个问题。理想情况下,我可以将回溯作为另一个管道分隔属性包含在与记录的其余消息相同的行上。

标签: python logging traceback


【解决方案1】:

您可以定义自己的Formatter,您可以覆盖其方法以完全按照您的需要格式化异常信息。这是一个简单(但有效)的示例:

import logging

class OneLineExceptionFormatter(logging.Formatter):
    def formatException(self, exc_info):
        result = super(OneLineExceptionFormatter, self).formatException(exc_info)
        return repr(result) # or format into one line however you want to

    def format(self, record):
        s = super(OneLineExceptionFormatter, self).format(record)
        if record.exc_text:
            s = s.replace('\n', '') + '|'
        return s

fh = logging.FileHandler('output.txt', 'w')
f = OneLineExceptionFormatter('%(asctime)s|%(levelname)s|%(message)s|', '%m/%d/%Y %I:%M:%S %p')
fh.setFormatter(f)
root = logging.getLogger()
root.setLevel(logging.DEBUG)
root.addHandler(fh)
logging.info('Sample message')

try:
    x = 1 / 0
except ZeroDivisionError as e:
    logging.exception('ZeroDivisionError: {0}'.format(e))

这只会产生两行:

01/28/2015 07:28:27 AM|INFO|Sample message|
01/28/2015 07:28:27 AM|ERROR|ZeroDivisionError: integer division or modulo by zero|'Traceback (most recent call last):\n  File "logtest2.py", line 23, in <module>\n    x = 1 / 0\nZeroDivisionError: integer division or modulo by zero'|

当然,你可以在这个例子的基础上做你想做的事,例如通过traceback 模块。

【讨论】:

  • 不知道为什么这里需要重写formatException,如果不需要用''包裹回溯,它什么都不做,你能解释一下为什么重写formatException方法吗?谢谢
  • @SunKe 因为异常本身可能包含换行符并且需要格式化为一行。在特定情况下,可能不需要它,但注释“或按您想要的方式格式化为一行”指示您可能需要更改的地方。
【解决方案2】:

对于我的用例,Vinay Sajip 的代码不够好(我使用更复杂的消息格式),所以我想出了这个(对我来说它也更干净):

class OneLineExceptionFormatter(logging.Formatter):
    def format(self, record):
        if record.exc_info:
            # Replace record.msg with the string representation of the message
            # use repr() to prevent printing it to multiple lines
            record.msg = repr(super().formatException(record.exc_info))
            record.exc_info = None
            record.exc_text = None
        result = super().format(record)
        return result

所以这个 format() 方法可以检测到一个异常将被记录,并且可以将其转换为它的字符串表示形式,并且日志消息的格式化仅针对该纯消息字符串发生。 我在 python 3 中对其进行了测试。

【讨论】:

  • 此方法不考虑来自原始日志记录调用的任何参数。例如:logging.exception("Unhandled exception when x=%s y=%s", "1", "2") 将失败。在调用format 之后应用格式化,因此在上面得到它的一种廉价方法是将formatException 结果附加到record.msg,例如:record.msg += repr(super().formatException(record.exc_info)) - 但总的来说,我认为接受的答案基于在官方文档上更好:docs.python.org/3/howto/…
【解决方案3】:

您应该定义自己的函数,该函数使用traceback.extract_tb 将回溯格式化为您想要的语法,然后将其返回或写入文件:

traceback.extract_tb(traceback[, limit])

返回从 t​​raceback 对象 traceback 中提取的最多限制“预处理”堆栈跟踪条目的列表。 这对于堆栈跟踪的替代格式很有用。如果省略限制或无,则提取所有条目。 “预处理”堆栈跟踪条目是一个 4 元组(文件名、行号、函数名、文本),表示通常为堆栈跟踪打印的信息。文本是一个去掉了前导和尾随空格的字符串;如果源不可用,则为None

https://docs.python.org/2/library/traceback.html

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-01-23
    • 2011-09-16
    • 2021-03-18
    • 1970-01-01
    • 2023-03-25
    • 1970-01-01
    • 2021-10-02
    相关资源
    最近更新 更多