为什么相同的utf-8字符串在打印中正常,而在日志中失败?

1 投票
2 回答
2447 浏览
提问于 2025-04-17 16:33

我需要手动做些什么来记录日志吗?因为打印(print)在后台会自动处理这些事情,以便记录utf-8字符串。

for line in unicodecsv.reader(cfile, encoding="utf-8"):
    for i in line:
        print "process_clusters: From CSV: %s" % i
        print "repr: %s" % repr(i)
        log.debug("process_clusters: From CSV: %s", i)

我的打印语句无论是拉丁字母的字符串还是俄语西里尔字母的字符串都能正常工作。

process_clusters: From CSV: escuchan
repr: u'escuchan'
process_clusters: From CSV: говоритъ
repr: u'\u0433\u043e\u0432\u043e\u0440\u0438\u0442\u044a'

但是,log.debug却不让我传入同样的变量。我收到了这个错误:

Traceback (most recent call last):
  File "/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/logging/__init__.py", line 765, in emit
    self.stream.write(fs % msg.encode("UTF-8"))
  File "/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/codecs.py", line 686, in write
    return self.writer.write(data)
  File "/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/codecs.py", line 351, in write
    data, consumed = self.encode(object, self.errors)
UnicodeDecodeError: 'ascii' codec can't decode byte 0xd0 in position 28: ordinal not in range(128)

我的日志、格式化器和处理器是:

log = logging.getLogger(__name__)
loglvl = getattr(logging, loglevel.upper()) # convert text log level to numeric
log.setLevel(loglvl) # set log level
handler = logging.FileHandler('inflection_finder.log', 'w', 'utf-8')
handler.setFormatter(logging.Formatter('[%(levelname)s] %(message)s'))
log.addHandler(handler)

我使用的是Python 2.6.7。

2 个回答

1

在Python2中,字符串和Unicode的处理真是一团糟……幸运的是,Python3已经修正了这个问题。不过,如果你不能升级到Python3,那就看看下面的内容吧。

我认为在使用logging时,关于编码有两种选择:

  1. 以二进制模式打开文件,所有字符串都作为字节字符串使用。
  2. 以文本模式打开文件,所有字符串都作为Unicode字符串使用。

其他的选择基本上都是行不通的!

问题在于,当你打开文件时指定了编码"utf-8",但你的俄文文本却是Unicode字符串。

所以你可以做以下其中一个(只有一个):

  1. 以二进制模式打开文件,也就是说,从FileStream的构造中去掉"utf-8"这个参数。
  2. 把所有相关的文本转换成Unicode字符串,包括传给log.debuglogging.Formatter的参数。
2

从错误信息来看,日志模块在写入信息之前试图对信息进行编码。这个信息被认为是ASCII字符串,但实际上它包含了UTF-8字符,所以不能被当作ASCII字符串。如果你在传递信息给日志记录器之前先把它转换成Unicode格式,可能就能解决这个问题。

    log.debug(u"process_clusters: From CSV: %s", i)

补充一下,我注意到你的参数字符串已经解码成Unicode了,所以我相应地更新了示例。

根据你最新的修改,你可能需要在设置中使用Unicode字符串:

handler.setFormatter(logging.Formatter(u'[%(levelname)s] %(message)s'))
                                     --^--

撰写回答