限制Python日志文件大小

3
我正在使用Python3 (Ubuntu 14.04)来编写日志文件。我想要限制日志文件的大小,因此选择了RotatingFileHandler。另外,我不想要轮换日志文件(.log变成.log.1等),我只是想要限制日志文件的大小。
我已经尝试过这样使用RotatingFileHandler:
filehandler = logging.handlers.RotatingFileHandler( "app.log", "a", 1000, 0, None, True )
logging.basicConfig( format = "%(asctime)s - %(name)s - %(levelname)s - %(message)s", level = logging.DEBUG, handlers = [ filehandler ] )

我发现日志文件会不断增长,并且在达到限制时不会被截断或“包装”。我原以为设置backupCount = 0会导致在写入日志消息之前检查日志文件大小,如果有足够的空间,则写入日志消息,否则清空文件并写出消息。你有什么想法吗?提前感谢您的回答。Bernmeister.
2个回答

5
如果您查看RotatingFileHandler的源代码,您会发现backupCount为0只是关闭然后重新打开日志流。这里有一个修改后的版本,可以实现您想要的功能,丢弃旧文件:
import os
import logging
import logging.handlers


class TruncatedFileHandler(logging.handlers.RotatingFileHandler):
    def __init__(self, filename, mode='a', maxBytes=0, encoding=None, delay=0):
        super(TruncatedFileHandler, self).__init__(
            filename, mode, maxBytes, 0, encoding, delay)

    def doRollover(self):
        """Truncate the file"""
        if self.stream:
            self.stream.close()
        dfn = self.baseFilename + ".1"
        if os.path.exists(dfn):
            os.remove(dfn)
        os.rename(self.baseFilename, dfn)
        os.remove(dfn)
        self.mode = 'w'
        self.stream = self._open()

以下是如何使用它:

filehandler = TruncatedFileHandler("app.log", "w", 1000)
logging.basicConfig(
    format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
    level=logging.DEBUG, handlers=[filehandler])

logger = logging.getLogger('spam_application')

for i in range(100):
    logger.debug("a log line, number: %s", i)

并证明它有效

❯❯❯ python3.4 main.py
❯❯❯ cat app.log*
2014-06-11 11:50:44,871 - spam_application - DEBUG - a log line, number: 91
2014-06-11 11:50:44,872 - spam_application - DEBUG - a log line, number: 92
2014-06-11 11:50:44,872 - spam_application - DEBUG - a log line, number: 93
2014-06-11 11:50:44,872 - spam_application - DEBUG - a log line, number: 94
2014-06-11 11:50:44,872 - spam_application - DEBUG - a log line, number: 95
2014-06-11 11:50:44,872 - spam_application - DEBUG - a log line, number: 96
2014-06-11 11:50:44,872 - spam_application - DEBUG - a log line, number: 97
2014-06-11 11:50:44,872 - spam_application - DEBUG - a log line, number: 98
2014-06-11 11:50:44,872 - spam_application - DEBUG - a log line, number: 99

1
@Bernmeister:请注意,此解决方案在滚动时完全删除旧日志。也许只需丢弃其中一些并继续追加即可,但我没有时间研究这个问题... - logc
doRollover() 中,为什么要删除文件 self.baseFilename + ".1"...我看不出除了 self.baseFilename 之外还会存在其他文件? - Bernmeister
@Bernmeister:我保留了这些语句,因为如果在os.renameos.remove语句之间停止Logger(例如,解释器崩溃),则可能存在剩余文件。但是我认为你的答案是正确的:只需删除baseFilename即可。 - logc

1
受logc答案启发,我采取了更为直接的方式:我只是删除了日志文件。
class TruncatedFileHandler( logging.handlers.RotatingFileHandler ):
    def __init__( self, filename, mode = "a", maxBytes = 0, encoding = None, delay = 0 ):
        super( TruncatedFileHandler, self ).__init__( filename, mode, maxBytes, 0, encoding, delay )


    def doRollover( self ):
        if self.stream: self.stream.close()

        if os.path.exists( self.baseFilename ): os.remove( self.baseFilename )

        self.mode = "a" # Not sure why but "w" behaves in the same way as "a".
        self.stream = self._open()

我发现我的解决方案和logc的解决方案有一个共同点,那就是一旦调用doRollover(),设置self.mode="a"self.mode="w"在写入文件方面没有区别。也就是说,我期望"w"每次从文件开头开始写入,但实际上它并不会这样做(它的行为与"a"相同,并从上次写入的位置开始写入)。


1
只是一个小注释:“a”的行为与“w”相同,因为如果文件不存在 - 而您刚刚删除了它 - 追加到文件与写入文件相同。在原始代码中,我认为他们从“a”更改为“w”,以确保在将所有其他文件滚动到其他名称后,从头开始创建文件。 - logc

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