Python/Django 日志记录的额外选项

2 投票
1 回答
1178 浏览
提问于 2025-04-18 02:18

我在Django里有一个日志配置文件,它会获取 sys.exc_info() 这个元组,并把它作为额外的选项在记录日志时使用(下面格式化器里的 typevalue 变量就是这样)。

'formatters': {
    'basic': {
        'format': '%(levelname)s %(lineno)d %(message)s %(type)s %(value)s'
    },
},

这是我记录错误的方式:

    except Exception, e:
        extra = {'type':sys.exc_info()[0], 'value':sys.exc_info()[1]}
        logger.warning('My message', extra=extra)                

但是如果我简单地写

    except Exception, e:
        logger.warning('My message')                                   

我会遇到一个异常,因为在格式化器里 typevalue 这两个变量现在是未定义的。我该怎么告诉格式化器,把这些变量当作可选的呢?也就是说,如果我在记录日志时传递了它们,就使用它们;如果没有,就跳过它们。

1 个回答

2

我建议你在记录日志时,不要把 exc_info 的各个部分直接传进去。其实,如果发生了异常,并且你传入了 exc_info=True 或者 exc_info=sys.exc_info(),那么这个元组已经存储在 LogRecordexc_info 属性里了。你可以在格式化器中访问这些信息,所以你可以使用一个 Formatter 的子类或者一个 Filter 的子类,把元组中的部分内容转换成其他的 LogRecord 属性,这样就可以在格式字符串中引用它们(或者通过自定义的 Formatter 子类来处理)。

更新:你可以在格式字符串中用 %(exc_info)s 来引用它,但这样只是会显示这个元组。需要注意的是,FormatterformatException 方法可以被重写,以便显示异常信息。默认情况下,它会格式化成标准的追踪信息——我理解你是想避免这种情况,对吧?

class MyFormatter(logging.Formatter):
    def formatException(self, exc_info):
        return 'exception: %s %s' % exc_info[:2])

这样会打印一行信息,而不是完整的追踪信息,但会在新的一行显示。或者你可以使用类似这样的方式:

class MyFormatter(logging.Formatter):
    def format(self, record):
        if isinstance(record.exc_info, tuple):
            record.exc_data = ' %s %s' % record.exc_info[:2]
        else:
            record.exc_data = ''
        return super(MyFormatter, self).format(record)

    def formatException(self, exc_info):
        return ''

然后在格式字符串中使用 %(exc_data)s

在这两种情况下,你需要确保在记录日志时传入真实的 exc_info,以确保异常数据被保存到 LogRecord 中。日志记录器的 exception() 方法会做到这一点(使用 ERROR 级别)。

进一步更新:如果你想用 Filter 的子类来实现,可以这样做:

class MyFilter(logging.Filter):
    def filter(self, record):
        if isinstance(record.exc_info, tuple):
            record.exc_data = ' %s %s' % record.exc_info[:2]
        else:
            record.exc_data = ''
        # The next bit is to prevent the standard Formatter from writing
        # a traceback
        record.exc_info = None
        return True # otherwise, the record doesn't get output

这应该和之前的例子效果一样,所以你可以使用相同的格式字符串。

撰写回答