如何在Python中将所有已记录的消息编码为utf-8

7
我有一个小型记录器函数,可以返回两个处理程序,以便同时记录到RotatingFileHandler和sys.stdout。
import os, logging, sys
from logging.handlers import RotatingFileHandler
from config import *

def get_logger(filename, log_level_stdout=logging.WARNING, log_level_file=logging.INFO, echo=True):
    logger = logging.getLogger(__name__)
    if not os.path.exists(PATH + '/Logs'):
        os.mkdir(PATH + '/Logs')

    logger.setLevel(logging.DEBUG)

    if echo:
        prn_handler = logging.StreamHandler(sys.stdout)
        prn_handler.setFormatter(logging.Formatter('%(asctime)s %(levelname)s: %(message)s'))
        prn_handler.setLevel(log_level_stdout)
        logger.addHandler(prn_handler)

    file_handler = RotatingFileHandler(PATH + '/Logs/' + filename, maxBytes=1048576, backupCount=3)
    file_handler.setFormatter(logging.Formatter('%(asctime)s %(levelname)s: %(message)s'))
    file_handler.setLevel(log_level_file)
    logger.addHandler(file_handler)
    return logger

一般情况下这个程序可以正常运行,但是记录日志时遇到某些字符串编码为cp1252,在使用日志函数将它们打印到标准输出(stdout)时会产生(非致命性)错误。需要指出的是,这些字符在错误信息中打印没有任何问题。将它们记录到文件中也不会出现问题。只有控制台 - sys.stdout - 会出现此错误。

--- Logging error ---
Traceback (most recent call last):
  File "C:\Program Files\Python38\lib\logging\__init__.py", line 1084, in emit
    stream.write(msg + self.terminator)
  File "C:\Program Files\Python38\lib\encodings\cp1252.py", line 19, in encode
    return codecs.charmap_encode(input,self.errors,encoding_table)[0]
UnicodeEncodeError: 'charmap' codec can't encode character '\u1ecd' in position 65: character maps to <undefined>
Call stack:
  File "script.py", line 147, in <module>
    logger.info(f"F-String with a name in it: '{name}'.")
Message: "F-String with a name in it: 'Heimstọð'."
Arguments: ()

解决方法是在调用日志记录函数的代码中对每一个获取的消息都进行UTF8编码,例如这样:
logger.info((f"F-String with a name in it: '{name}'.").encode('utf8'))

但我觉得这既不优雅也不高效。 需要注意的是,文件日志记录运行良好,我已经尝试在Windows系统变量中设置PYTHONIOENCODING为utf-8,但没有明显的效果。

更新: 事实证明我很蠢。仅因为控制台打印出错误消息并不意味着控制台打印是错误的根本原因。我正在研究其他问题的答案,这些问题已经被推荐给我,过了一会儿我意识到我对函数的“if echo”部分所做的任何更改都没有影响结果。最后一次检查是注释掉整个块,我仍然得到错误。那时,我意识到问题实际上是由于在写入文件时未强制执行UTF8而引起的。按照@michael-ruth建议的方法将简单的kwarg encoding ='utf-8'添加到RotatingFileHandler中解决了我的问题。 PS 我不确定如何处理这种情况,因为虽然那个答案解决了我的问题,但它并不是我最初询问或该问题所建议的内容,因为我最初误解了根本原因。我仍将其视为解决方案,并赞同两个答案。我还将编辑问题,以免误导将来的读者认为它将回答该问题,而实际上并非如此。


1
这个回答解决了你的问题吗?如何在Python 3中设置sys.stdout编码? - Michael Ruth
2个回答

4

实例化处理程序时设置编码方式,而不是显式地对消息进行编码。

file_handler = RotatingFileHandler(
    PATH + '/Logs/' + filename, 
    maxBytes=1048576, 
    backupCount=3,
    encoding='utf-8'
)

help(RotatingFileHandler)是您最好的朋友。

在logging.handlers模块中,RotatingFileHandler类的帮助信息:
class RotatingFileHandler(BaseRotatingHandler) | RotatingFileHandler(filename, mode='a', maxBytes=0, backupCount=0, encoding=None, delay=False)

1
这看起来很漂亮,我可能会在以后使用它,但它并没有解决我的问题,我的问题在于StreamHandler和sys.stdout。 我意识到这在我的原始帖子中有些不清楚(而且我还拼错了stdout为stdio),所以我修复了帖子和标题以更准确地反映问题。对此给您带来的不便表示歉意。 - Midnight

2

你能检查一下sys.stdout (sys.stdout.encoding)的编码吗? 如果不是'utf-8'这个答案或许可以帮助重新配置编码。


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