为什么Python日志框架会丢失消息?

时间:2016-10-03 19:00:44

标签: python python-3.x logging python-requests error-logging

我有一个Python 3.4应用程序,它广泛使用日志记录。我有两个FileHandler和一个StreamHandler注册。一切都按预期工作,除了有时,似乎在requests库抛出异常后发生,日志文件丢失所有累积的消息并从新消息开始。我假设由于某种原因,FileHandler使用mode='w'重新打开了文件,但我不明白为什么。有什么想法吗?

主程序按如下方式设置记录器:

# Set up root logger - two handlers logging to files
fh_debug = logging.FileHandler('Syncer.debug', mode='w', encoding='utf-8')
fh_debug.setLevel(logging.DEBUG)
fh_log = logging.FileHandler('Syncer.log', mode='w', encoding='utf-8')
fh_log.setLevel(logging.INFO)
fh_formatter = logging.Formatter(
    '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
fh_debug.setFormatter(fh_formatter)
fh_log.setFormatter(fh_formatter)
logging.getLogger().addHandler(fh_debug)
logging.getLogger().addHandler(fh_log)

# Add console handler with a higher log level
ch = logging.StreamHandler()
ch.setLevel(logging.INFO)
ch_formatter = logging.Formatter('%(message)s')
ch.setFormatter(ch_formatter)
logging.getLogger().addHandler(ch)

# Need to set the logging level of the logger as well as the handlers
logging.getLogger().setLevel(logging.DEBUG)

# Set up the logger for this module
logger = logging.getLogger("Syncer")

logger.debug('Logger started.')

模块只包含

logger = logging.getLogger(__name__)

2 个答案:

答案 0 :(得分:0)

您的问题是您为mode选择了错误的FileHandler

默认模式FileHandlera,这意味着将新行添加到日志文件中。

  

class logging.FileHandler(filename,mode =' a',encoding = None,delay = False)

您将默认模式修改为w,将文件截断为零长度或创建新文件进行写入。这就是你丢失所有累积消息的原因。

将其更改为mode='a'或只删除mode='w',然后您的记录器即可使用。

阅读official python docs here

答案 1 :(得分:0)

发生这种情况会导致Python退出,因为logging模块被(部分)关闭,导致日志文件被关闭,但是requests试图记录某些内容,因此记录器会重新打开日志文件-不幸的是,将其截断。

来自the source code

class FileHandler(StreamHandler):
    # [...]

    def close(self):
         # [...]
         self.stream = None
         # [...]

    def _open(self):
        return open(self.baseFilename, self.mode, encoding=self.encoding,
                    errors=self.errors)

    def emit(self, record):
        if self.stream is None:
            self.stream = self._open()
        StreamHandler.emit(self, record)

我认为最简单的解决方法是在程序启动时截断日志文件,并告诉logging以追加模式打开文件:

open('Syncer.debug', mode='w').close() # truncate
fh_debug = logging.FileHandler('Syncer.debug', mode='a', encoding='utf-8')
fh_debug.setLevel(logging.DEBUG)
open('Syncer.log', mode='w').close() # truncate
fh_log = logging.FileHandler('Syncer.log', mode='a', encoding='utf-8')
fh_log.setLevel(logging.INFO)