Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/298.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 子进程stdout/stderr到有限大小日志文件_Python_Unix_Logging_File Io_Subprocess - Fatal编程技术网

Python 子进程stdout/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

我有一个与stderr进行大量聊天的流程,我想将这些内容记录到一个文件中

foo 2> /tmp/foo.log
实际上,我是用python
subprocess.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)