Python 子进程stdout/stderr到有限大小日志文件
我有一个与stderr进行大量聊天的流程,我想将这些内容记录到一个文件中Python 子进程stdout/stderr到有限大小日志文件,python,unix,logging,file-io,subprocess,Python,Unix,Logging,File Io,Subprocess,我有一个与stderr进行大量聊天的流程,我想将这些内容记录到一个文件中 foo 2> /tmp/foo.log 实际上,我是用pythonsubprocess.Popen启动它的,但出于这个问题的目的,它也可能来自shell with open('/tmp/foo.log', 'w') as stderr: foo_proc = subprocess.Popen(['foo'], stderr=stderr) 问题是几天后,我的日志文件可能非常大,比如>500MB。我对所有的st
foo 2> /tmp/foo.log
实际上,我是用pythonsubprocess.Popen
启动它的,但出于这个问题的目的,它也可能来自shell
with open('/tmp/foo.log', 'w') as stderr:
foo_proc = subprocess.Popen(['foo'], stderr=stderr)
问题是几天后,我的日志文件可能非常大,比如>500MB。我对所有的stderr
聊天感兴趣,但只对最近的东西感兴趣。我如何将日志文件的大小限制为,比如说,1MB?该文件应该有点像一个循环缓冲区,因为最新的内容将被写入,但较旧的内容应该从文件中掉出来,这样它就永远不会超过给定的大小
我不确定是否有一种优雅的Unixey方式可以做到这一点,而我只是不知道,使用某种特殊的文件
只要我不必中断运行过程,使用日志循环的替代解决方案也足以满足我的需要 使用循环缓冲区的方法将很难实现,因为一旦出现问题,您必须不断重写整个文件 使用logrotate或其他方法将是您的选择。在这种情况下,您只需执行与此类似的操作:
import subprocess
import signal
def hupsignal(signum, frame):
global logfile
logfile.close()
logfile = open('/tmp/foo.log', 'a')
logfile = open('/tmp/foo.log', 'a')
signal.signal()
foo_proc = subprocess.Popen(['foo'], stderr=subprocess.PIPE)
for chunk in iter(lambda: foo_proc.stderr.read(8192), ''):
# iterate until EOF occurs
logfile.write(chunk)
# or do you want to rotate yourself?
# Then omit the signal stuff and do it here.
# if logfile.tell() > MAX_FILE_SIZE:
# logfile.close()
# logfile = open('/tmp/foo.log', 'a')
这不是一个完整的解决方案;将其视为伪代码,因为它未经测试,我不确定其中一个地方的语法。可能它需要一些修改才能工作。但是你应该明白
同样,这是一个如何使其与logrotate一起工作的示例。当然,如果需要,您可以自己旋转日志文件。使用循环缓冲区的方法将很难实现,因为一旦出现问题,您就必须不断重写整个文件 使用logrotate或其他方法将是您的选择。在这种情况下,您只需执行与此类似的操作:
import subprocess
import signal
def hupsignal(signum, frame):
global logfile
logfile.close()
logfile = open('/tmp/foo.log', 'a')
logfile = open('/tmp/foo.log', 'a')
signal.signal()
foo_proc = subprocess.Popen(['foo'], stderr=subprocess.PIPE)
for chunk in iter(lambda: foo_proc.stderr.read(8192), ''):
# iterate until EOF occurs
logfile.write(chunk)
# or do you want to rotate yourself?
# Then omit the signal stuff and do it here.
# if logfile.tell() > MAX_FILE_SIZE:
# logfile.close()
# logfile = open('/tmp/foo.log', 'a')
这不是一个完整的解决方案;将其视为伪代码,因为它未经测试,我不确定其中一个地方的语法。可能它需要一些修改才能工作。但是你应该明白
同样,这是一个如何使其与logrotate一起工作的示例。当然,如果需要,您可以自己旋转日志文件。您可以使用“打开文件描述”(与“打开文件描述符”不同,但与之密切相关)的属性。特别是,当前写入位置与打开文件描述相关联,因此共享单个打开文件描述的两个进程可以各自调整写入位置 因此,在上下文中,原始进程可以保留子进程标准错误的文件描述符,并在位置达到1 MiB大小时,定期将指针重新定位到文件的开头,从而实现所需的循环缓冲区效果 最大的问题是确定当前消息的写入位置,这样您就可以从最旧的材料(就在文件位置的前面)读取到最新的材料。覆盖旧行的新行不太可能完全匹配,因此会有一些碎片。您可以使用已知的字符序列(例如“XXXXXX”)跟随子项的每一行,然后重新定位子项的每一次写入以覆盖上一个标记…但这肯定需要控制正在运行的程序。如果它不在您的控制之下,或者无法修改,则该选项将消失 另一种方法是定期截断文件(可能在复制文件之后),并让子进程以追加模式写入(因为文件是以追加模式在父进程中打开的)。您可以安排在截断之前将材料从文件复制到备用文件,以保留以前的1 MiB数据。这样您最多可以使用2个MiB,这比500个MiB好得多,如果您实际空间不足,可以配置大小
玩得开心 您可以使用“打开文件描述”(与“打开文件描述符”不同,但与之密切相关)的属性。特别是,当前写入位置与打开文件描述相关联,因此共享单个打开文件描述的两个进程可以各自调整写入位置 因此,在上下文中,原始进程可以保留子进程标准错误的文件描述符,并在位置达到1 MiB大小时,定期将指针重新定位到文件的开头,从而实现所需的循环缓冲区效果 最大的问题是确定当前消息的写入位置,这样您就可以从最旧的材料(就在文件位置的前面)读取到最新的材料。覆盖旧行的新行不太可能完全匹配,因此会有一些碎片。您可以使用已知的字符序列(例如“XXXXXX”)跟随子项的每一行,然后重新定位子项的每一次写入以覆盖上一个标记…但这肯定需要控制正在运行的程序。如果它不在您的控制之下,或者无法修改,则该选项将消失 另一种方法是定期截断文件(可能在复制文件之后),并让子进程以追加模式写入(因为文件是以追加模式在父进程中打开的)。您可以安排在截断之前将材料从文件复制到备用文件,以保留以前的1 MiB数据。这样您最多可以使用2个MiB,这比500个MiB好得多,如果您实际空间不足,可以配置大小
玩得开心 您应该能够使用stdlib日志记录包来完成此操作。您可以执行以下操作,而不是将子流程的输出直接连接到文件:
import logging
logger = logging.getLogger('foo')
def stream_reader(stream):
while True:
line = stream.readline()
logger.debug('%s', line.strip())
这只是记录从流接收到的每一行,您可以使用RotatingFileHa配置日志记录
import logging, logging.handlers
handler = logging.handlers.RotatingFileHandler('/tmp/foo.log', 'a', 100000, 10)
logging.getLogger().addHandler(handler)
logging.getLogger('foo').setLevel(logging.DEBUG)