Python日志字符串格式化

12

我正在使用Python的日志格式化器(formatter)来格式化日志记录(log records),我的fmt值为

fmt = "[%(filename)s:%(lineno)s] %(message)s"

我希望的是将“[file.py:20]”这个内容拉伸到指定的宽度(比如10个字符)。如果仅仅是一个值的话很容易实现,但是有没有一种方法可以将整个结构都拉伸到指定长度呢?

tmp = "[%(filename)s:%(lineno)s]"
fmt = "%(tmp)10s %(message)s"

我想知道是否可以使用字符串格式化来实现,或者我是否可以以某种方式欺骗Python的格式化程序以获得我想要的结果..


2
不,除非通过子类化Formatter来创建新的格式化程序,否则这是不可能的。这对您有多重要? - S.Lott
我会花费大约1-2小时来让它工作(虽然我有点新手)。我假设我需要覆盖格式方法并在LogRecord对象中注入自定义属性? - alexcepoi
10个字符是“最小”宽度吗?即,如果字符串超过10个字符,您是否想要截断它? - Rob Cowie
不是非常重要,但假设不要截断它。 - alexcepoi
我发现自己很惊讶,为什么在这种情况下我不能使用.ljust()。难道这个字符串不再是一个字符串了吗? - nu everest
3个回答

13
作为一个例子,这个格式化器通过截断文件名或在行号后面右侧填充空格"[%(filename)s:%(lineno)s]" 来确保固定宽度。
class MyFormatter(logging.Formatter):
    width = 10

    def format(self, record):
        max_filename_width = self.width - 3 - len(str(record.lineno))
        filename = record.filename
        if len(record.filename) > max_filename_width:
            filename = record.filename[:max_filename_width]
        a = "%s:%s" % (filename, record.lineno)
        return "[%s] %s" % (a.ljust(self.width), record.msg)

if __name__ == '__main__':
    logger = logging.getLogger('simple_example')
    logger.setLevel(logging.DEBUG)
    ch = logging.StreamHandler()
    ch.setLevel(logging.DEBUG)
    formatter = MyFormatter()
    ch.setFormatter(formatter)
    logger.addHandler(ch)

    logger.debug('No one expects the spammish repetition')

编辑:

如果你想确保最小字符宽度为10个字符,那就放弃文件名相关的东西吧。

def format(self, record):
    a = "%s:%s" % (record.filename, record.lineno)
    return "[%s] %s" % (a.ljust(self.width), record.msg)

9

选项1

从这里开始:http://docs.python.org/library/logging.html#formatter-objects

您将创建自己的自定义Formatter子类,该子类提供了自己独特的format方法。

然后,您必须确保在每个Handlers中调用setFormatter(),以便它们使用您的新格式化程序。

选项2

创建带有附加属性的LogRecord的子类。

子类化Logger并覆盖makeRecord以创建您的LogRecord的新子类。

提供一个自定义格式,其中使用此新属性值。


2

基于@rob-cowie的回答,我发现以下内容很有用:

class MyFormatter(logging.Formatter):
    width = 24
    datefmt='%Y-%m-%d %H:%M:%S'

    def format(self, record):
        cpath = '%s:%s:%s' % (record.module, record.funcName, record.lineno)
        cpath = cpath[-self.width:].ljust(self.width)
        record.message = record.getMessage()
        s = "%-7s %s %s : %s" % (record.levelname, self.formatTime(record, self.datefmt), cpath, record.getMessage())
        if record.exc_info:
            # Cache the traceback text to avoid converting it multiple times
            # (it's constant anyway)
            if not record.exc_text:
                record.exc_text = self.formatException(record.exc_info)
        if record.exc_text:
            if s[-1:] != "\n":
                s = s + "\n"
            s = s + record.exc_text
        #if record.stack_info:
        #    if s[-1:] != "\n":
        #        s = s + "\n"
        #    s = s + self.formatStack(record.stack_info)
        return s

logFormatter = MyFormatter()
logger = logging.getLogger("example")
logger.setFormatter(logFormatter)

这将会输出如下内容:

WARNING 2014-03-28 16:05:09 module:function:31       : Message
WARNING 2014-03-28 16:05:09 dule:longerfunctions:140 : Message

当执行logger.setFormatter(logFormatter)时发生异常:AttributeError: 'Logger'对象没有属性'setFormatter'。 - dehiker

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接