0

Как использовать UTF-8 в логировании Python?

134

Я пытаюсь записать строку, закодированную в 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 ответ(ов)

0

Проблема, которую вы описали, возникает из-за того, что строка формата является байтовой строкой, в то время как некоторые аргументы строки формата — это строки 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")

Эти изменения позволят избежать ошибок кодирования, и ваше сообщение будет корректно отображаться в журнале.

0

У меня была похожая проблема с запуском 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', # <-- Это и было тем, чего не хватало.
        ....
    },
},

Надеюсь, это поможет кому-то избежать тех же ошибок!

0

Если вы используете Python 3.7 или более позднюю версию, перед выполнением вашего Python-скрипта установите переменную окружения PYTHONUTF8 в значение 1.

Например, для Linux выполните следующую команду:

export PYTHONUTF8=1

Для PowerShell используйте:

$env:PYTHONUTF8 = "1"

В командной строке Windows выполните:

set PYTHONUTF8=1

После этого просто выполните ваш Python-скрипт.

0

Попробуйте следующее:

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, но, похоже, это значение по умолчанию, или что-то другое происходит, так как код работает и без этого.

0

В 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' греческий текст корректно сохраняется в файл лога. Также сообщения будут выводиться на стандартный вывод (консоль) в реальном времени.

Чтобы ответить на вопрос, пожалуйста, войдите или зарегистрируйтесь