[英]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.