在Django中使用Python日志和Unicode时的问题
现在我完全搞糊涂了……我正在用Python/Django开发应用,并且使用Python的日志功能。我的应用需要支持Unicode,所有的模型都有一个unicode()方法,返回的是u'..'这样的格式。最近在记录日志时,我遇到了一个非常奇怪的问题,花了很长时间才发现可以重现这个问题。我尝试了Python 2.5.5和2.6.4,结果都是一样。
每当我进行一些简单的日志记录,比如:
logging.debug(u'new value %s' % group)
这会调用模型的group.unicode()方法:返回unicode(group.name)
我的unicode方法都长这样:
def __unicode__(self):
return u'%s - %s (%s)' % (self.group, self.user.get_full_name(), self.role)
即使group.name是XXX或ÄÄÄ(需要Unicode),这也能正常工作。但是当我想记录一个集合、列表、字典,或者Django查询集时,列表中的某些实例可能是Unicode,也可能不是,这就出问题了……
所以当group.name需要Unicode,比如Luleå(我家乡的名字),我就会遇到UnicodeDecodingError的错误。
logging.debug(u'new groups %s' % list_of_groups)
通常我会收到这样的错误:
Exception Type: UnicodeDecodeError
Exception Value: ('ascii', '<RBACInstanceRoleSet: s2 | \xc3\x84\xc3\x96\xc3\x96\xc3\x85\xc3\x85\xc3\x85 Gruppen>]', 106, 107, 'ordinal not in range(128)')
但是如果我执行print list_of_groups
,在终端上显示的内容就很好。
所以,我的理解是,列表开始生成自己,并对所有元素调用repr(),然后它们返回自己的值——在这种情况下应该是's2 | ÅÄÖ',然后列表以(ascii,列表中的内容)的形式呈现,当尝试将ascii解码成Unicode时,这当然是行不通的——因为列表中的一个元素在调用repr时返回了u'...'。
但这到底是为什么???
而且为什么当我记录简单的东西,比如group.name或者group时,Unicode和ascii都能正确处理,而一旦我懒得去记录列表、集合或其他东西时,只要遇到Unicode字符就会出问题……
还有一些工作和失败的例子。如果group.name
我去模型字段,group
调用__unicode__()
logging.debug("1. group: %s " % group.name) # WORKS
logging.debug(u"2. group: %s " % group) # WORKS
logging.debug("3. group: %s " % group) # FAILS
logging.debug(u"4. group: %s " % group.name) # WORKS
logging.debug("5. group: %s " % group.name) # WORKS
……我真的以为我对Unicode有了很好的理解;-(
9 个回答
试着把这段代码放在你的 views.py 文件的最上面。
#-*- coding: utf-8 -*-
...
我无法通过简单的测试重现你的问题:
Python 2.6.4 (r264:75706, Dec 7 2009, 18:45:15) [GCC 4.4.1] on linux2 Type "help", "copyright", "credits" or "license" for more information. >>> import logging >>> group = u'Luleå' >>> logging.warning('Group: %s', group) WARNING:root:Group: Luleå >>> logging.warning(u'Group: %s', group) WARNING:root:Group: Luleå >>>
所以,正如Daniel所说,你传给日志的内容中可能有一些不符合Unicode标准的东西。
另外,我不知道你使用了什么处理器,但请确保如果有文件处理器的话,你要明确指定输出的编码方式。如果有流处理器的话,也要把需要编码的输出流用像codecs
模块提供的编码包装器包裹起来(然后把这个包装后的流传给日志)。
这是我的测试代码:
#-*- coding: utf-8 -*-
class Wrap:
def __init__(self, s): self.s = s
def __repr__(self): return repr(self.s)
def __unicode__(self): return unicode(self.s)
def __str__(self): return str(self.s)
s = 'hello' # a plaintext string
u = 'ÅÄÖÖ'.decode('utf-8')
l = [s,u]
test0 = unicode(repr(l))
test1 = 'string %s' % l
test2 = u'unicode %s' % l
上面的代码运行得很好。不过,如果你把repr的定义改成:
def repr(self): return unicode(self.s)
那么就会出现错误:
Traceback (most recent call last):
File "mytest.py", line 13, in <module> unicode(l)
UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-3:
ordinal not in range(128)
看起来在对象的层级结构中,有一个repr()的实现不对,它错误地返回了一个unicode字符串,而不是普通字符串。正如其他人提到的,当你使用格式化字符串,比如:
'format %s' % mylist
而且mylist是一个序列时,python会自动对它调用repr(),而不是unicode()(因为没有“正确”的方法将列表表示为unicode字符串)。
可能是django出了问题,或者你在某个模型中错误地实现了__repr__
。