如何在Python日志中使用UTF-8?

53 投票
9 回答
76039 浏览
提问于 2025-04-15 14:57

我正在尝试使用Python的日志记录工具把一个UTF-8编码的字符串写入文件。举个简单的例子:

import logging

def logging_test():
    handler = logging.FileHandler("/home/ted/logfile.txt", "w",
                                  encoding = "UTF-8")
    formatter = logging.Formatter("%(message)s")
    handler.setFormatter(formatter)
    root_logger = logging.getLogger()
    root_logger.addHandler(handler)
    root_logger.setLevel(logging.INFO)

    # This is an o with a hat on it.
    byte_string = '\xc3\xb4'
    unicode_string = unicode("\xc3\xb4", "utf-8")

    print "printed unicode object: %s" % unicode_string

    # Explode
    root_logger.info(unicode_string)

if __name__ == "__main__":
    logging_test()

在调用logging.info()的时候,这个代码会报出UnicodeDecodeError的错误。

从底层来看,Python的日志记录工具是用codecs这个工具来打开日志文件的,并且把“UTF-8”作为编码格式传进去。这本身没有问题,但它试图把字节字符串写入文件,而不是写入Unicode对象,这就出错了。简单来说,Python实际上是这样做的:

file_handler.write(unicode_string.encode("UTF-8"))

但它应该是这样做的:

file_handler.write(unicode_string)

这是Python的一个bug吗,还是我在胡思乱想?顺便说一下,我用的是标准的Python 2.6版本。

9 个回答

14

我来得有点晚,但我刚看到这个帖子,它让我很容易就设置了utf-8格式的日志记录。

这是帖子链接

或者这里是代码:

root_logger= logging.getLogger()
root_logger.setLevel(logging.DEBUG) # or whatever
handler = logging.FileHandler('test.log', 'w', 'utf-8') # or whatever
formatter = logging.Formatter('%(name)s %(message)s') # or whatever
handler.setFormatter(formatter) # Pass handler as a parameter, not assign
root_logger.addHandler(handler)
35

代码如下:

raise Exception(u'щ')

导致了:

  File "/usr/lib/python2.7/logging/__init__.py", line 467, in format
    s = self._fmt % record.__dict__
UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-3: ordinal not in range(128)

这个问题出现的原因是,格式字符串是字节字符串,而一些格式字符串的参数是包含非ASCII字符的unicode字符串:

>>> "%(message)s" % {'message': Exception(u'\u0449')}
*** UnicodeEncodeError: 'ascii' codec can't encode character u'\u0449' in position 0: ordinal not in range(128)

将格式字符串改为unicode格式可以解决这个问题:

>>> u"%(message)s" % {'message': Exception(u'\u0449')}
u'\u0449'

所以,在你的日志配置中,确保所有的格式字符串都是unicode格式:

'formatters': {
    'simple': {
        'format': u'%(asctime)-s %(levelname)s [%(name)s]: %(message)s',
        'datefmt': '%Y-%m-%d %H:%M:%S',
    },
 ...

并且修改默认的logging格式化器,使其使用unicode格式字符串:

logging._defaultFormatter = logging.Formatter(u"%(message)s")
16

检查一下你是否安装了最新的 Python 2.6,因为自从 2.6 发布以来,发现并修复了一些 Unicode 的错误。例如,在我的 Ubuntu Jaunty 系统上,我运行了你复制粘贴的脚本,只去掉了日志文件名中的 '/home/ted/' 前缀。结果(从终端窗口复制粘贴过来的):

vinay@eta-jaunty:~/projects/scratch$ python --version
Python 2.6.2
vinay@eta-jaunty:~/projects/scratch$ python utest.py 
printed unicode object: ô
vinay@eta-jaunty:~/projects/scratch$ cat logfile.txt 
ô
vinay@eta-jaunty:~/projects/scratch$ 

在 Windows 系统上:

C:\temp>python --version
Python 2.6.2

C:\temp>python utest.py
printed unicode object: ô

还有文件的内容:

alt text

这也可能解释了为什么 Lennart Regebro 也无法重现这个问题。

撰写回答