Как использовать 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)
# Это буква "о" с надстрочной чертой.
byte_string = '\xc3\xb4'
unicode_string = unicode("\xc3\xb4", "utf-8")
print "напечатанный объект unicode: %s" % unicode_string
# Ошибка
root_logger.info(unicode_string)
if __name__ == "__main__":
logging_test()
Этот код выдает ошибку UnicodeDecodeError
при вызове logging.info()
.
На более низком уровне пакет логирования Python использует пакет codecs для открытия файла журнала, передавая "UTF-8" в качестве кодировки. Это хорошая практика, но он пытается записать байтовые строки в файл вместо объектов unicode, что приводит к ошибке. По сути, Python делает следующее:
file_handler.write(unicode_string.encode("UTF-8"))
Хотя на самом деле должно быть так:
file_handler.write(unicode_string)
Это ошибка в Python или я что-то делаю не так? Для справки, это стандартная установка Python 2.6.
5 ответ(ов)
Проблема, которую вы описали, возникает из-за того, что строка формата является байтовой строкой, в то время как некоторые аргументы строки формата — это строки Unicode с не-ASCII символами. В вашем случае, когда вы пытаетесь обработать сообщение об исключении с ненормативным символом (в данном случае, 'щ'), Python пытается закодировать его в ASCII, что и приводит к ошибке UnicodeEncodeError
.
Чтобы исправить эту ошибку, нужно убедиться, что все строки формата в вашей конфигурации журналирования являются строками Unicode. В вашем коде можно просто сделать строки формата с префиксом u
, как это показано ниже:
'formatters': {
'simple': {
'format': u'%(asctime)-s %(levelname)s [%(name)s]: %(message)s',
'datefmt': '%Y-%m-%d %H:%M:%S',
},
...
}
Кроме того, вы можете изменить стандартный форматтер для журналирования, чтобы использовать формат строк Unicode:
import logging
logging._defaultFormatter = logging.Formatter(u"%(message)s")
Эти изменения позволят избежать ошибок кодирования, и ваше сообщение будет корректно отображаться в журнале.
У меня была похожая проблема с запуском Django на Python 3: мой логгер перестал работать при возникновении некоторых умлаутов (äöüß), хотя в остальном проблем не возникало. Я просмотрел множество решений, но ничего не сработало. Я попробовал следующий код:
import locale;
if locale.getpreferredencoding().upper() != 'UTF-8':
locale.setlocale(locale.LC_ALL, 'en_US.UTF-8')
Этот код я нашел в одном из комментариев. Он не помог. Проверив текущую локаль, я обнаружил какой-то странный ANSI-кодировка, которая в основном означала "ASCII". Это отвело меня в совершенно неверном направлении.
Изменение строк формата логирования на Юникод не сработало. Установка "магического" комментария с кодировкой в начале скрипта тоже не помогла. Настройка кодировки на сообщении отправителя (текст пришел из HTTP-запроса) не дала результата.
Но то, что сработало — это установка кодировки для файлового обработчика на UTF-8 в settings.py
. Поскольку у меня ничего не было настроено, по умолчанию оно оказалось None
, что, как выяснилось, соответствует ASCII (или, как мне хотелось бы подумать, ASS-KEY).
'handlers': {
'file': {
'level': 'DEBUG',
'class': 'logging.handlers.TimedRotatingFileHandler',
'encoding': 'UTF-8', # <-- Это и было тем, чего не хватало.
....
},
},
Надеюсь, это поможет кому-то избежать тех же ошибок!
Если вы используете Python 3.7 или более позднюю версию, перед выполнением вашего Python-скрипта установите переменную окружения PYTHONUTF8 в значение 1.
Например, для Linux выполните следующую команду:
export PYTHONUTF8=1
Для PowerShell используйте:
$env:PYTHONUTF8 = "1"
В командной строке Windows выполните:
set PYTHONUTF8=1
После этого просто выполните ваш Python-скрипт.
Попробуйте следующее:
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)
# Это буква "о" с циркумфлексом над ней.
byte_string = '\xc3\xb4'
unicode_string = unicode("\xc3\xb4", "utf-8")
print "Выведенный объект unicode: %s" % unicode_string
# Логируем
root_logger.info(unicode_string.encode("utf8", "replace"))
if __name__ == "__main__":
logging_test()
Что касается вашего вопроса, я ожидал, что придется использовать codecs.open
, чтобы открыть файл с кодировкой UTF-8, но, похоже, это значение по умолчанию, или что-то другое происходит, так как код работает и без этого.
В Python 3.10 я смог записывать юникодные символы (в моем случае греческие буквы) в лог, добавив параметр encoding='utf-8'
.
Вот небольшой пример:
import logging
import sys
if __name__ == "__main__":
logging.basicConfig(filename="log.log", filemode="w", level=logging.DEBUG, encoding="utf-8")
root = logging.getLogger()
root.setLevel(logging.DEBUG)
handler = logging.StreamHandler(sys.stdout)
handler.setLevel(logging.DEBUG)
formatter = logging.Formatter(" %(levelname)s - %(message)s") # %(asctime)s - %(name)s -
handler.setFormatter(formatter)
root.addHandler(handler)
logging.debug("Γεια σου μαρία")
Этот код создает лог-файл с именем log.log
, в который записываются сообщения уровня DEBUG и выше. Благодаря параметру encoding='utf-8'
греческий текст корректно сохраняется в файл лога. Также сообщения будут выводиться на стандартный вывод (консоль) в реальном времени.
Как зарегистрировать ошибку в Python с отладочной информацией?
Конфигурация логгера для записи в файл и вывода на stdout
Настройка логирования в Python: вывод всех сообщений в stdout и файл журнала
Как окрасить вывод логирования Python?
Как заставить логгер удалять существующий файл журнала перед записью в него?