在Django中使用Python日志记录和Unicode时出现问题

7

现在我有些困惑了...我正在使用python/django进行开发,并使用python logging。我的所有应用程序都需要unicode,而我的所有模型只实现了一个unicode(),返回u'...'的方法。现在当我记录日志时,我遇到了一个非常奇怪的问题,它花费了很长时间才发现我可以重现它。我尝试过Py 2.5.5和Py 2.6.4,但结果相同。

每当我进行一些简单的记录,例如:

logging.debug(u'new value %s' % group) 

这个调用模型组unicode()方法,返回模型组名称的unicode编码。

我的所有unicode方法都是这样的:

def __unicode__(self):
    return u'%s - %s (%s)' % (self.group, self.user.get_full_name(), self.role)

即使group.name是XXX或ÄÄÄ(需要Unicode),这也有效。但是,当我因某种原因想要记录集合、列表、字典、Django查询集以及例如列表中的单个实例时,它们可能是Unicode或非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方法时,就可以正确处理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,但是现在感觉有点力不从心。


哦,忘了。我的生产网站使用Postgres,我在本地测试/开发时使用Sqlite,但是无论在哪里都遇到了同样的问题。 - jenlu
你只有在日志机制方面遇到这个问题吗?如果你注释掉它会发生什么? - luc
是的 - 只有记录。打印相同的语句也可以,注释日志也可以。现在注意到使用logging.debug(list_of_groups)会显示带有Unicode字符的漂亮日志消息... - jenlu
9个回答

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__方法。


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所说,你传递给logging的内容中可能存在某些不正确的Unicode字符。

此外,我不知道你正在使用什么处理器,但请确保如果有文件处理器,则明确指定要使用的输出编码,如果有流处理器,则还应将任何需要的输出流用编码包装器(如codecs模块提供的那样)进行包装(并将包装后的流传递给logging)。


是的,你说得没错。但我的数据存储在Django模型中,我处理这些实例时,当打印一个组(即模型类Group的实例)的名称和许多其他字段时,我只需print model--这将调用group的__unicode__方法来打印或记录自己。而且这总是适用于实例。但是,当我有这样的实例列表/集合/字典并在debug.logging中输出它们时,如果其中一个实例返回unicode,则会崩溃。不过,如果没有其他方法解决这个问题,我将检查所有的日志记录语句并进行调整 :-( - jenlu
关键在于当我使用debug.loggin('%s' % list_of_groups)时,列表在“聚集自身”时声明它是ASCII,并添加来自repr()的单个元素。尝试对列表的单个元素调用repr(),然后我得到u'Luleå'等等。如果只有列表在“聚集自身”时能够确定它不是ASCII,那么它就不会尝试将自己解码为Unicode,因为它已经是Unicode了,我就不会遇到这个问题了... - jenlu

0

检查:

import locale
locale.getpreferredencoding()

必须是 'utf8' 编码。我使用的是 'cp1252'。

帮助我添加到 manage.py:

import _locale
_locale._getdefaultlocale = (lambda *args: ['en_US', 'utf8'])

Windows 10,Django 1.10.3,Python 3.5.2, 修复了俄语语言的问题


0

我最终按照答案中的建议,检查了所有代码,并在尝试记录集合/列表/字典/Django查询集时使用列表推导式或类似方法。因此,像这样适应并添加东西解决了我的问题:

logging.debug(u"new groups: %s" % [unicode(g) for g in list_of_groups])

所以现在我要做的就是记住永远不能忘记做这个;-)

0

如果你明白我的意思,我不明白你不理解的是什么。你的中间段落:

所以,我的理解是列表开始生成并对其所有元素执行repr(),它们返回它们的值 - 在这种情况下应该是's2 | ÅÄÖÖ',然后列表呈现为(ascii,列表中的内容),然后尝试将ascii解码为unicode当然不起作用--因为列表中的一个元素在repr时返回了u'...'。

完全解释了正在发生的事情 - 输出列表并不等同于打印列表中的所有元素,因为在幕后它只是调用列表中每个元素的repr()。与其输出原始列表,您可以记录一个列表推导式,该推导式对每个元素调用unicode,这将修复它。


是的 - 但我有些懒惰,希望能够只输出任何东西; 列表、字段等等,而且我有很多代码需要检查警告/信息/调试/错误,并且需要查看每个语句并进行适应。不过,如果这是必要的话,那就... - jenlu
1
是的,还有另一件事,我首先尝试做的是:logging.debug('new groups %s' % list_of_groups) # 突然起作用即删除u',然后所有列表/集合/字典都开始记录得很好,但是我记录直接字段的所有其他语句都开始出现相同的错误。因此,例如将不再起作用:logging.debug('new group with name %s' % group.name) # 失败 - jenlu

0

1
欢迎来到stackoveflow.com。请在回答问题时更加精确,您可以在这里阅读有关如何撰写良好答案的指南 - Tahir Raza

0

你尝试手动将任何结果转换为Unicode了吗?

logging.debug(u'new groups %s' % unicode(list_of_groups("UTF-8"))

0

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