Python未释放日志文件句柄

68 投票
4 回答
74394 浏览
提问于 2025-04-17 19:13

我有一个应用程序需要进行多次模拟运行。我想设置一个日志记录机制,把所有的日志记录都放到一个叫做general.log的文件里,而每次模拟运行的日志则放到run00001.log、run00002.log等文件中。为此,我定义了一个叫Run的类。在这个类的__init__()方法里,我为每次运行的日志添加了一个新的文件句柄。

问题是,这些运行的日志文件句柄从来没有被释放,所以经过几次运行后,系统可用的句柄就用完了,导致程序崩溃。

我设置了一些例程来测试这个问题,具体如下:

主程序

import Model
try:
    myrun = Model.Run('20130315150340_run_49295')
    ha = raw_input('enter')
    myrun.log.info("some info")
except:
    traceback.print_exc(file=sys.stdout)

ha = raw_input('enter3')

Run类在模块Model中定义,具体如下:

import logging
class Run(object):

    """ Implements the functionality of a single run. """
    def __init__(self, runid):
        self.logdir="."
        self.runid          = runid
        self.logFile        = os.path.join(self.logdir , self.runid + '.log')
        self.log            = logging.getLogger('Run'+self.runid)
        myformatter         = logging.Formatter('%(asctime)s %(name)-12s %(levelname)-8s %(message)s')
        myhandler      = logging.FileHandler(self.logFile)
        myhandler.setLevel(logging.INFO)
        myhandler.setFormatter(myformatter)
        self.log.addHandler(myhandler) 

然后我使用程序进程查看器来跟踪文件句柄。我看到运行日志文件出现了,但从来没有消失。

有没有办法强制释放这些文件句柄呢?

4 个回答

0

我们可能只想对 FileHandler 进行 .close() 操作,而不对其他的进行关闭,所以可以稍微修改一下被接受的答案,变成这样:

for handler in self.log.handlers:
    if isinstance(handler, logging.FileHandler):
        handler.close()

另外,对于一些简单的情况,比如我们只是用 logging.basicConfig() 配置了一个日志记录器,可以通过调用 logging.getLogger().handlers 来获取处理器。

41

你还可以完全关闭日志记录。这样的话,文件句柄就会被释放:

logging.shutdown()

它会关闭所有配置的日志处理器所打开的句柄。

我需要这样做是为了在单元测试结束后能够删除日志文件,而我可以在调用logging.shutdown()方法后立刻删除它。

116

你需要在文件处理器上调用 .close() 方法。

当你的 Run 类完成时,记得调用:

handlers = self.log.handlers[:]
for handler in handlers:
    self.log.removeHandler(handler)
    handler.close()

每当有新的日志信息到来时,文件处理器会自动重新打开之前设置的文件名,所以调用 handler.close() 有时候看起来没什么用。把处理器从日志记录器中移除,可以阻止未来的日志记录发送到它;在上面的代码中,我们先这样做,以避免其他线程在不合适的时候重新打开处理器。

这里还有个答案建议你使用 logging.shutdown()。不过,logging.shutdown() 实际上只是调用 handler.flush()handler.close(),我个人不太推荐使用它。因为使用后,日志模块会处于一种状态,让你不能可靠地再次使用 logging.shutdown()

撰写回答