2011-08-23 54 views
1

我有一個過程,它跟stderr聊天很多,我想將這些東西記錄到一個文件中。子過程stdout/stderr到有限大小日誌文件

foo 2> /tmp/foo.log 

其實我與Python subprocess.Popen啓動它,但它也可以被從外殼爲這一問題的目的。

with open('/tmp/foo.log', 'w') as stderr: 
    foo_proc = subprocess.Popen(['foo'], stderr=stderr) 

的問題是,幾天後我的日誌文件可能會非常大,像> 500 MB。我對stderr聊天感興趣,但只有最近的東西。我如何限制日誌文件的大小,比如1 MB?該文件應該有點像循環緩衝區,因爲最近的東西將被寫入,但舊的東西應該從文件中刪除,以便它永遠不會超過給定的大小。

我不確定是否有一種優雅的Unixey方法可以做到這一點,我只是不知道,用某種特殊文件。

只要我不必中斷正在運行的進程,使用日誌輪轉的替代解決方案也足以滿足我的需求。

回答

3

您應該能夠使用STDLIB日誌包來做到這一點。相反,子輸出直接連接到一個文件中,你可以做這樣的事情:

import logging 

logger = logging.getLogger('foo') 

def stream_reader(stream): 
    while True: 
     line = stream.readline() 
     logger.debug('%s', line.strip()) 

這只是記錄從流接收的每一行,你可以配置一個RotatingFileHandler提供日誌文件循環日誌記錄。然後您安排閱讀這些數據並記錄下來。

foo_proc = subprocess.Popen(['foo'], stderr=subprocess.PIPE) 

thread = threading.Thread(target=stream_reader, args=(foo_proc.stderr,)) 
thread.setDaemon(True) # optional 
thread.start() 

# do other stuff 

thread.join() # await thread termination (optional for daemons) 

當然你也可以撥打stream_reader(foo_proc.stderr)過,但我假設你可能有其他的工作要做,而富子做它的東西。

這裏是你可以配置日誌(代碼應該只執行一次)的一種方法:

import logging, logging.handlers 

handler = logging.handlers.RotatingFileHandler('/tmp/foo.log', 'a', 100000, 10) 
logging.getLogger().addHandler(handler) 
logging.getLogger('foo').setLevel(logging.DEBUG) 

這將創造多達100K命名foo.log的10個文件(和旋轉foo.log.1後,foo.log.2等,其中foo.log是最新的)。您也可以通過在1000000 1給你只是foo.log和foo.log.1,在旋轉時發生該文件將大小超過百萬字節。

1

使用循環緩衝區的方式很難實現,因爲一旦出現問題,您將不得不重寫整個文件。

與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

您可能能夠使用「打開文件描述」(不同於「打開文件描述符但與之緊密相關」)的屬性。特別是,當前寫入位置與打開文件描述相關聯,所以共享單個打開文件描述的兩個進程可以分別調整寫入位置。

因此,在上下文中,原始進程可以保留子進程標準錯誤的文件描述符,並定期在位置達到1 MiB大小時,將指針重新定位到文件開始位置,從而實現您的要求循環緩衝效果。

最大的問題是確定當前消息的寫入位置,以便您可以從最早的材料(位於文件位置的前面)讀取最新的材料。覆蓋舊線的新線不太可能完全匹配,所以會出現一些碎片。你也許可以按照已知的字符序列(比如'XXXXXX')跟蹤孩子的每一行,然後讓孩子重新定位以覆蓋前一個標記......但是這肯定需要控制正在執行的程序跑。如果它不在您的控制之下,或者無法修改,則該選項將消失。

另一種方法是定期截斷文件(可能在複製之後),並讓子進程以附加模式寫入(因爲文件在父文件以附加模式打開)。您可以安排在截斷前將文件中的材料複製到備用文件,以保留前面的1 MiB數據。您可以以這種方式使用最多2個MiB,這比500 MiB好得多,如果實際上空間不足,可以配置尺寸。

玩得開心!