我有一个小记录器函数,它可能返回两个处理程序以同时记录到 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 中编码的,并且在尝试通过记录器功能将它们打印到标准输出时抛出(非致命)错误。应该注意的是,在错误消息中可以打印完全相同的字符。将它们记录到文件中也不会导致任何问题。只有控制台 - 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 解决了我的问题。 附:我不确定如何处理这种情况,因为虽然这个答案解决了我的问题,但这并不是我真正想要的,也不是问题所建议的,因为我最初误解了根本原因。我仍然会检查它作为解决方案并对两个答案都投赞成票。我还将编辑这个问题,以免误导未来的读者相信它会回答这个问题,但事实并非如此。
在实例化处理程序时设置编码,而不是显式编码消息。
file_handler = RotatingFileHandler(
PATH + '/Logs/' + filename,
maxBytes=1048576,
backupCount=3,
encoding='utf-8'
)
help(RotatingFileHandler)
是你最好的朋友。
模块logging.handlers中RotatingFileHandler类的帮助: 类 RotatingFileHandler(BaseRotatingHandler) | RotatingFileHandler(文件名,模式='a',maxBytes=0,backupCount=0,编码=无,延迟=False)
如果有人会在这里搜索日志对象设置解决方案(如在 Django 中),语法如下:
"file": {
"class": "logging.FileHandler",
"formatter": "verbose",
"filename": "app.log",
"mode": "w",
"encoding": 'utf-8',
},
请注意,所有其他处理程序都没有此问题,并且也无法在它们上设置编码。