我正在尝试从 python 2.7 中的库中记录各种异常。我发现有时异常包含一个 unicode 字符串,有时包含一个 utf8 字节串。我认为 logging.exception(e)
是记录它们的正确方法,但以下似乎不起作用:
# encoding: utf-8
import logging
try:
raise Exception('jörn')
except Exception as e:
logging.exception(e)
try:
raise Exception(u'jörn')
except Exception as e:
logging.exception(e)
将其保存到文件中并运行它会产生以下结果:
$ python test.py
ERROR:root:jörn
Traceback (most recent call last):
File "test.py", line 4, in <module>
raise Exception('jörn')
Exception: jörn
Traceback (most recent call last):
File "/usr/local/Cellar/python/2.7.10/Frameworks/Python.framework/Versions/2.7/lib/python2.7/logging/__init__.py", line 859, in emit
msg = self.format(record)
File "/usr/local/Cellar/python/2.7.10/Frameworks/Python.framework/Versions/2.7/lib/python2.7/logging/__init__.py", line 732, in format
return fmt.format(record)
File "/usr/local/Cellar/python/2.7.10/Frameworks/Python.framework/Versions/2.7/lib/python2.7/logging/__init__.py", line 474, in format
s = self._fmt % record.__dict__
UnicodeEncodeError: 'ascii' codec can't encode character u'\xf6' in position 1: ordinal not in range(128)
Logged from file test.py, line 12
因此,正如您所见,utf8 异常工作正常,但 unicode 异常破坏了日志记录,吞没了真正的异常并将其隐藏在 UnicodeEncodeError
之后。
是否有一些不会破坏我的代码的异常的标准日志记录工具?我错过了什么?
最佳答案
其实,我想我终于自己找到了错误和正确的方法:我似乎使用了logging.exception('msg')
一直错您不是要传递异常,而是要传递一条消息:
# encoding: utf-8
import logging
try:
raise Exception('jörn')
except Exception as e:
logging.exception('exception occurred')
try:
raise Exception(u'jörn')
except Exception as e:
logging.exception('exception occurred')
正确运行上面的代码会记录异常:
$ python test.py
ERROR:root:exception occurred
Traceback (most recent call last):
File "test.py", line 4, in <module>
raise Exception('jörn')
Exception: jörn
ERROR:root:exception occurred
Traceback (most recent call last):
File "test.py", line 10, in <module>
raise Exception(u'jörn')
Exception: j\xf6rn
logging.exception(e)
似乎失败的原因是它将异常 e
向上传递到 logging.Formatter.format()
它作为 record.message
变量到达的地方仍然是一个 Exception
对象。
然后在第 474 行发生以下情况:
s = self._fmt % record.__dict__
相当于以下内容:
s = '%(levelname)s:%(name)s:%(message)s' % {
'levelname': 'ERROR',
'name': 'ROOT',
'message': Exception(u'jörn')
}
事实证明,这就是为什么如果 message
是 ['jörn', u'jörn', Exception('jörn')]
之一它会工作,而如果不是它是 Exception(u'jörn')
:
>>> 'foo %s' % 'jörn'
'foo j\xc3\xb6rn'
>>> 'foo %s' % u'jörn'
u'foo j\xf6rn'
>>> 'foo %s' % Exception('jörn')
'foo j\xc3\xb6rn'
>>> 'foo %s' % Exception(u'jörn')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
UnicodeEncodeError: 'ascii' codec can't encode character u'\xf6' in position 1: ordinal not in range(128)
如您所见,unicode 字符串会发生自动向上转换,这就是以下工作的原因:
>>> logging.error('jörn')
ERROR:root:jörn
>>> logging.error(u'jörn')
ERROR:root:jörn
尝试使用未正确处理其消息编码的 Exception
对象时,这种到 unicode 的转换失败(遗憾的是,在很多库中似乎都是这种情况)。
logging.exception(msg)
调用似乎正确地使用了 repr()
来格式化日志记录的异常,并在其前面加上您的 msg
。因此,如果您没有犯错并将异常传递给 logging.exception
,它将正确记录它。
长话短说:
不要使用 logging.exception(e)
而要使用 logging.exception('exception occurred')
。它会自动并正确地将格式化的异常附加到您的日志中。如果您真的想在不采用某种编码的情况下使用异常消息,最安全的做法是 logging.exception(repr(e))
。
关于python - 在 python 2 中正确记录 unicode 和 utf-8 异常,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31137568/