UTF-8 В протоколе Python, как?
Я пытаюсь записать кодированную строку UTF-8 в файл, используя пакет протоколирования Python. В качестве примера игрушки:
import logging
def logging_test():
handler = logging.FileHandler("/home/ted/logfile.txt", "w",
encoding = "UTF-8")
formatter = logging.Formatter("%(message)s")
handler.setFormatter(formatter)
root_logger = logging.getLogger()
root_logger.addHandler(handler)
root_logger.setLevel(logging.INFO)
# This is an o with a hat on it.
byte_string = '\xc3\xb4'
unicode_string = unicode("\xc3\xb4", "utf-8")
print "printed unicode object: %s" % unicode_string
# Explode
root_logger.info(unicode_string)
if __name__ == "__main__":
logging_test()
Это взрывается с помощью UnicodeDecodeError при вызове logging.info().
На более низком уровне пакет протоколов Python использует пакет кодеков для открытия файла журнала, передавая в качестве аргумента аргумент "UTF-8". Это хорошо и хорошо, но он пытается записать строки байтов в файл вместо объектов unicode, которые взрываются. По сути, Python делает это:
file_handler.write(unicode_string.encode("UTF-8"))
Когда это должно быть сделано:
file_handler.write(unicode_string)
Является ли это ошибкой в Python, или я беру сумасшедшие таблетки? FWIW, это запасная установка Python 2.6.
Ответы
Ответ 1
Убедитесь, что у вас есть последний Python 2.6 - некоторые ошибки Unicode были найдены и исправлены с тех пор, как вышел 2.6. Например, в моей системе Ubuntu Jaunty я скопировал и вставил ваш script, удалив только префикс '/home/ted/' из имени файла журнала. Результат (скопирован и вставлен из окна терминала):
[email protected]:~/projects/scratch$ python --version
Python 2.6.2
[email protected]:~/projects/scratch$ python utest.py
printed unicode object: ô
[email protected]:~/projects/scratch$ cat logfile.txt
ô
[email protected]:~/projects/scratch$
В окне Windows:
C:\temp>python --version
Python 2.6.2
C:\temp>python utest.py
printed unicode object: ô
И содержимое файла:
![alt text]()
Это также может объяснить, почему Леннарт Регебро не смог воспроизвести его.
Ответ 2
Имея код вроде:
raise Exception(u'щ')
Вызванный:
File "/usr/lib/python2.7/logging/__init__.py", line 467, in format
s = self._fmt % record.__dict__
UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-3: ordinal not in range(128)
Это происходит потому, что строка формата является байтовой строкой, в то время как некоторые аргументы строки форматирования являются строками unicode с символами, отличными от ASCII:
>>> "%(message)s" % {'message': Exception(u'\u0449')}
*** UnicodeEncodeError: 'ascii' codec can't encode character u'\u0449' in position 0: ordinal not in range(128)
Создание строки формата unicode устраняет проблему:
>>> u"%(message)s" % {'message': Exception(u'\u0449')}
u'\u0449'
Итак, в вашей конфигурации ведения журнала сделайте всю строку формата unicode:
'formatters': {
'simple': {
'format': u'%(asctime)-s %(levelname)s [%(name)s]: %(message)s',
'datefmt': '%Y-%m-%d %H:%M:%S',
},
...
И исправьте форматтера logging
по умолчанию, чтобы использовать строку формата unicode:
logging._defaultFormatter = logging.Formatter(u"%(message)s")
Ответ 3
Попробуйте следующее:
import logging
def logging_test():
log = open("./logfile.txt", "w")
handler = logging.StreamHandler(log)
formatter = logging.Formatter("%(message)s")
handler.setFormatter(formatter)
root_logger = logging.getLogger()
root_logger.addHandler(handler)
root_logger.setLevel(logging.INFO)
# This is an o with a hat on it.
byte_string = '\xc3\xb4'
unicode_string = unicode("\xc3\xb4", "utf-8")
print "printed unicode object: %s" % unicode_string
# Explode
root_logger.info(unicode_string.encode("utf8", "replace"))
if __name__ == "__main__":
logging_test()
Для чего я должен был использовать codecs.open, чтобы открыть файл с помощью кодировки utf-8, но либо по умолчанию, либо что-то еще происходит здесь, так как он работает так, как будто.
Ответ 4
Если я правильно понял вашу проблему, такая же проблема возникнет в вашей системе, когда вы это сделаете:
str(u'ô')
Я предполагаю, что автоматическая кодировка кодировки локали в Unix не будет работать до тех пор, пока вы не включите ветвь if
, поддерживающую языковой стандарт в setencoding
, в site
через locale
. Обычно этот файл находится в /usr/lib/python2.x
, он все равно проверяет. AFAIK, locale-aware setencoding
отключен по умолчанию (это верно для моей установки Python 2.6).
Возможные варианты:
- Пусть система определит правильный способ кодирования строк Unicode в байтах или сделать это в вашем коде (требуется некоторая конфигурация в специфичном для сайта
site.py
).
- Кодировать строки Unicode в вашем коде и выводить только байты
См. также Иллюзионное кодирование setdefault с помощью Ian Bicking и связанных ссылок.
Ответ 5
У меня была похожая проблема с запуском Django в Python3: мой регистратор умер при столкновении с некоторыми умлаутами (äöüß), но в остальном все было в порядке. Я просмотрел много результатов и не нашел ничего работающего. Я старался
import locale;
if locale.getpreferredencoding().upper() != 'UTF-8':
locale.setlocale(locale.LC_ALL, 'en_US.UTF-8')
который я получил из комментария выше. Это не работает. Глядя на текущую локализацию, я получил сумасшедшую вещь ANSI, которая в основном означала просто "ASCII". Это отправило меня в совершенно неверное русло.
Изменение формата строки журнала на Unicode не поможет. Установка магического комментария кодировки в начале журнала не поможет. Установка кодировки в сообщении отправителя (текст пришел из HTTP-запроса) не помогла.
Что DID работало, так это установило кодировку для обработчика файлов в UTF-8 в settings.py
. Поскольку у меня ничего не было установлено, по умолчанию будет None
. Который, очевидно, заканчивается ASCII (или, как мне хотелось бы думать: ASS-KEY)
'handlers': {
'file': {
'level': 'DEBUG',
'class': 'logging.handlers.TimedRotatingFileHandler',
'encoding': 'UTF-8', # <-- That was missing.
....
},
},