[英]Python multiprocessing - logging.FileHandler object raises PicklingError
[英]Python multiprocessing+logging.FileHandler
我正在嘗試在多處理服務器中實現日志記錄。 根據文檔,“不支持從多個進程記錄到單個文件”。 我創建了一個小程序來檢查這個語句:
import logging
import multiprocessing
import os
log = logging.getLogger()
def setup_logger():
formatter = logging.Formatter('%(asctime)s %(name)s %(levelname)s: %(message)s')
fileHandler = logging.FileHandler('test.log')
fileHandler.setFormatter(formatter)
log.setLevel(logging.DEBUG)
log.addHandler(fileHandler)
def write_log_entries(identifier, start_event):
start_event.wait()
for i in range(100):
s = ''.join(str(identifier) for k in range(30))
log.info('[{}, {}] --- {}'.format(os.getpid(), identifier, s))
if __name__ == '__main__':
setup_logger()
procs = []
start_event = multiprocessing.Event()
for i in range(100, 300):
p = multiprocessing.Process(target=write_log_entries, args=(i, start_event))
procs.append(p)
for p in procs:
p.start()
start_event.set()
for p in procs:
p.join()
在執行上面的代碼后,我希望在“test.log”中看到一個完整的混亂,但一切似乎都很好(當然,時間戳除外,它們不是按順序)。
任何人都可以解釋為什么當多個進程同時寫入日志文件時,日志條目是否重疊? 在這種情況下,log.info()可以被認為是原子的嗎?
簡短的回答:內核鎖定單個調用write
,所以只要消息很小就可以,所以只需一次write
就可以刷新,這樣write
就能成功寫入所有內容。 沒有一般保證是這種情況,這就是為什么文檔不承諾這將工作的原因。
答案很長:每次調用log.info
刷新日志輸出。 這是必要的,否則您將無法在文件中看到最新的日志條目。 在Python / libc級別,flush實現為對write(2)系統調用的調用,調用該調用以寫出文件緩沖區的內容(如果有)。 在您的情況下,緩沖區內容是您的日志消息。 因此,Python或libc(取決於正在使用的文件)最終會調用OS調用,例如:
write(fd, buf, buflen);
...其中fd
是日志文件的系統級文件描述符, buf
是寫入緩沖的內存, buflen
是消息的長度。 (如果使用諸如Linux上的strace
的工具跟蹤Python進程,則可以看到這些調用。) write
返回成功寫入的字符數,並且內核不會將這些字符與文件的同一區域中的其他寫入交錯。 如果在O_APPEND模式下打開文件,則寫入甚至保證在文件末尾,至少在Unix上。 因此,如果buflen
很小,就像普通日志消息一樣,一切都很好。 但至少有兩件事可能出錯。
首先,不能保證所有buflen
都將在一次write
。 write
可以被信號中斷, fd
可以指向接受固定大小寫入的設備,或者您的日志消息可能足夠大,內核不能在單個塊中接受它。 通常情況下,這不是問題 - 正確寫入的write
總是作為循環實現。 但在你的情況下,這將是一場災難,因為不同的write
調用將與其他進程交錯。
其次,如果您的日志消息足夠大以至於它不適合stdio緩沖區(8K左右),那么它將在到達內核之前被拆分為塊。 在記錄回溯時,或將日志格式化為XML等詳細格式時,很容易發生這種情況。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.