使用python日志记录时查找格式错误的来源

使用python日志记录时查找格式错误的来源,python,debugging,logging,stack-trace,Python,Debugging,Logging,Stack Trace,当我有很多不同的模块使用标准python日志模块时,下面的堆栈跟踪几乎不能帮助我找到我的日志语句的确切位置: Traceback (most recent call last): File "/usr/lib/python2.6/logging/__init__.py", line 768, in emit msg = self.format(record) File "/usr/lib/python2.6/logging/__init__.py", line 648, in f

当我有很多不同的模块使用标准python日志模块时,下面的堆栈跟踪几乎不能帮助我找到我的日志语句的确切位置:

Traceback (most recent call last):
  File "/usr/lib/python2.6/logging/__init__.py", line 768, in emit
    msg = 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 all arguments converted during string formatting

我只是开始使用python的日志模块,所以我可能忽略了一些显而易见的东西。我不确定堆栈跟踪是否无用,因为我使用的是greenlets,或者这对于日志模块来说是否正常,但如果有任何帮助,我将不胜感激。我愿意修改源代码,任何可以使日志库实际给出问题所在的线索的东西。

日志模块设计用于阻止坏日志消息杀死其余代码,因此
emit
方法捕获错误并将其传递给方法
handleError
。最简单的方法是临时编辑
/usr/lib/python2.6/logging/\uuuu init\uuuuu.py
,然后查找
handleError
。它看起来像这样:

def handleError(self, record):
    """
    Handle errors which occur during an emit() call.

    This method should be called from handlers when an exception is
    encountered during an emit() call. If raiseExceptions is false,
    exceptions get silently ignored. This is what is mostly wanted
    for a logging system - most users will not care about errors in
    the logging system, they are more interested in application errors.
    You could, however, replace this with a custom handler if you wish.
    The record which was being processed is passed in to this method.
    """
    if raiseExceptions:
        ei = sys.exc_info()
        try:
            traceback.print_exception(ei[0], ei[1], ei[2],
                                      None, sys.stderr)
            sys.stderr.write('Logged from file %s, line %s\n' % (
                             record.filename, record.lineno))
        except IOError:
            pass    # see issue 5971
        finally:
            del ei
    def handleError(record):
        raise RuntimeError(record)
    handler.handleError = handleError

现在暂时编辑它。在开始时插入一个简单的
raise
,可以确保错误在代码中传播,而不是被吞没。解决问题后,只需将日志代码恢复到原来的状态。

您还可以发现如下错误,而不是编辑已安装的python代码:

def handleError(self, record):
    """
    Handle errors which occur during an emit() call.

    This method should be called from handlers when an exception is
    encountered during an emit() call. If raiseExceptions is false,
    exceptions get silently ignored. This is what is mostly wanted
    for a logging system - most users will not care about errors in
    the logging system, they are more interested in application errors.
    You could, however, replace this with a custom handler if you wish.
    The record which was being processed is passed in to this method.
    """
    if raiseExceptions:
        ei = sys.exc_info()
        try:
            traceback.print_exception(ei[0], ei[1], ei[2],
                                      None, sys.stderr)
            sys.stderr.write('Logged from file %s, line %s\n' % (
                             record.filename, record.lineno))
        except IOError:
            pass    # see issue 5971
        finally:
            del ei
    def handleError(record):
        raise RuntimeError(record)
    handler.handleError = handleError

其中handler是给出问题的处理程序之一。现在,当发生格式错误时,您将看到位置。

或者,您可以创建自己的格式设置程序,但随后必须将其包含在所有位置

class DebugFormatter(logging.Formatter):
    def format(self, record):
        try:
            return super(DebugFormatter, self).format(record)
        except:
            print "Unable to format record"
            print "record.filename ", record.filename
            print "record.lineno ", record.lineno
            print "record.msg ", record.msg
            print "record.args: ",record.args
            raise


FORMAT = '%(levelname)s %(filename)s:%(lineno)d  %(message)s'
formatter = DebugFormatter(FORMAT)
handler = logging.StreamHandler()
handler.setLevel(logging.DEBUG)
handler.setFormatter(formatter)
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)
logger.addHandler(handler)

这并不是对这个问题的真正回答,但希望其他像我这样使用日志模块的初学者也能回答这个问题

我的问题是我用logging.info替换了所有出现的打印, 所以像
print('a',a)
这样的有效行变成了
logging.info('a',a)
(但它应该是
logging.info('a%s'%a)


这一点在研究中也得到了暗示,但在研究中没有出现同样的问题
这种回溯是由于错误的格式名称引起的。因此,在为日志文件创建格式时,请在python文档中检查一下格式名称:“

好吧,这是一种可怕的方法。然而,对于那些完全不可能编辑库的情况,这是一种值得记住的方法,所以+1。@porgarmingduod:猴子补丁可能不太好,但它没有比操纵标准库的源代码更可怕的方法了。猴子补丁将影响到系统中的其他一切同样的过程,但是对库的直接编辑将影响同一系统上的所有其他内容。python 3.6中的新语法:
logging.info(f'a{a}')
谢谢,我还没有研究过它!