在Django中使用Python日志和Unicode时的问题

7 投票
9 回答
7727 浏览
提问于 2025-04-15 18:19

现在我完全搞糊涂了……我正在用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 个回答

1

试着把这段代码放在你的 views.py 文件的最上面。

#-*- coding: utf-8 -*-
...
1

我无法通过简单的测试重现你的问题:

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模块提供的编码包装器包裹起来(然后把这个包装后的流传给日志)。

2

这是我的测试代码:

#-*- 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__

撰写回答