在Python日志库中轻松查找“参数不足...”的方法

7 投票
2 回答
3135 浏览
提问于 2025-04-15 20:36

你知道有什么简单的方法可以找到导致“格式字符串参数不足”的日志调用吗?在我的工作站上,我修改了 logging/__init__.py 文件,让它打印出消息,这样我就能轻松找到源代码中的那一行。

但是在测试环境中,你不能更改 Python 标准库,也不能轻松运行调试工具,你有什么想法吗?

注意:错误追踪信息没有意义,因为它是被日志库捕获的。以下是一个错误追踪信息:

  File "/usr/lib/python2.6/logging/handlers.py", line 71, in emit
    if self.shouldRollover(record):
  File "/usr/lib/python2.6/logging/handlers.py", line 144, in shouldRollover
    msg = "%s\n" % self.format(record)
  File "/usr/lib/python2.6/logging/__init__.py", line 648, in format
    return fmt.format(record)
  File "/usr/lib/python2.6/logging/__init__.py", line 436, in format
    record.message = record.getMessage()
  File "/usr/lib/python2.6/logging/__init__.py", line 306, in getMessage
    msg = msg % self.args
TypeError: not enough arguments for format string

这是标准库中捕获错误的代码:

    try:
        if self.shouldRollover(record):
            self.doRollover()
        logging.FileHandler.emit(self, record)
    except (KeyboardInterrupt, SystemExit):
        raise
    except:
        self.handleError(record)

根据 Alex 的建议的解决方案:我对 getMessage 进行了封装,以打印消息和参数。以下是代码:

def print_log_record_on_error(func):
    def wrap(self, *args, **kwargs):
        try:
            return func(self, *args, **kwargs)
        except:
            import sys
            print >>sys.stderr, "Unable to create log message msg=%r, args=%r " % (
                                getattr(self, 'msg', '?'), getattr(self, 'args', '?'))
            raise
    return wrap
import logging
logging.LogRecord.getMessage = print_log_record_on_error(logging.LogRecord.getMessage)

2 个回答

0

为了找到一个日志调用,它会报错“格式字符串的参数不够”。

我在源代码中搜索所有的日志调用。其实不多,主要有这些:.info(.warn(.debug(.error(.critical(.exception(.log(

这些方法中有一个格式不对,参数不够。通常很容易发现,因为消息里会有“%”这样的格式符号。

如果你的应用程序在调用所有日志记录器时都使用logger,那就更简单了,因为你可以直接搜索logger.

2

一般来说,捕捉异常(包括你提到的那种)最好的方法是把可疑的代码放在一个 tryexcept 语句的 try 部分;在 except 部分,你可以使用 traceback 模块或者其他方法来找到出错的具体代码。

如果不处理的异常会进入 sys.excepthook,这也是你可以用来获取错误追踪信息的一种方式。

不过,我觉得 logging 模块没有一个明确的方法来告诉它“让异常继续传播”——在这种情况下,虽然有点不太干净,但你可能需要用到一种叫做猴子补丁(monkey-patching)的方法。换句话说,你通常可以在运行时“修改 Python 标准库”——通过把其中的标识符设置为你自己的函数、类等,来包裹标准的那些。例如,如果你知道问题出在 logging.warning 的调用上,一个“概念验证”的猴子补丁可能是:

import logging, traceback

orgwarn = logging.warning
def mywarn(msg, *a):
  try: res = msg % a
  except TypeError:
    traceback.print_exc()
  return orgwarn(msg, *a)
logging.warning = mywarn

当然,还有更干净的方法,但可能会有点麻烦(比如,自己创建一个日志类并将其设置为根日志)。猴子补丁在生产环境中并不是一个好的做法(因为它对系统升级很脆弱,理解和维护起来也比较困难等),但在调试和测试时可以接受。

撰写回答