python - 为什么我在多个进程中得到相同的记录器
问题描述
从不同进程登录到单个文件时,我得到了重复的日志。为什么我在多个进程中得到相同的记录器?记录器有一些处理程序。
我的代码:
def get_queue_logger(q):
qh = QueueHandler(q)
root = logging.getLogger()
print(f"Logger: {id(root)}, Handlers: {root.handlers}")
root.addHandler(qh)
return root
def main():
q = Manager().Queue()
pool = Pool()
for i in range(10):
pool.apply(
func=get_queue_logger,
args=(q,)
)
pool.close()
pool.join()
if __name__ == "__main__":
main()
安慰:
Logger: 2041926265200, Handlers: []
Logger: 2095359404400, Handlers: []
Logger: 1781876664688, Handlers: []
Logger: 3222115264880, Handlers: []
Logger: 2041926265200, Handlers: [<QueueHandler (NOTSET)>] # The same logger
Logger: 1705732548976, Handlers: []
Logger: 2505966954864, Handlers: []
Logger: 2095359404400, Handlers: [<QueueHandler (NOTSET)>] # The same logger
Logger: 1934246029680, Handlers: []
Logger: 1698863065456, Handlers: []
解决方案
我也遇到了这种行为,同时使用multiprocessing.Pool
和concurrent.futures.process.ProcessPoolExecutor
。QueueHandler
handler
如果您通过创建进程,它会按预期工作(即,每个进程的根记录器中添加一个) multiprocessing.Process
,例如将您的更改main()
为:
q = Manager().Queue()
workers = []
for i in range(10):
worker = multiprocessing.Process(target=get_queue_logger,
args=(q,))
workers.append(worker)
worker.start()
for w in workers:
w.join()
我认为使用 a 时额外的(和不需要的)处理程序pool
是由于一个记录器实例已经在另一个进程中为函数QueueHandler
handler
添加了可用的get_queue_logger
,但我不明白如何(编辑 - 参见下面的 EDIT#2)。顺便说一句,当使用pool
whenmultiprocessing.set_start_method
设置为“spawn”或“fork”时,我看到了这个问题。
这也意味着此处文档中的第一个代码示例:https : //docs.python.org/3/howto/logging-cookbook.html#logging-to-a-single-file-from-multiple-processes 没有使用此处提到的方法时无法按预期工作:https ://docs.python.org/3/howto/logging-cookbook.html#using-concurrent-futures-processpoolexecutor 。
编辑#1:请注意,这个问题(根记录器的不同且有时重复的 id,QueueHandler
handler
有时每个进程不止一次添加)似乎只发生在 MacOS(10.15.7)上。Ubuntu 19.10 上的相同代码会为每个记录器生成相同的id,并且QueueHandler
每个进程只会添加一次。
编辑#2:事实证明,QueueHandler
handler
当使用池时,每个进程向根记录器添加多个是由于向池提交的任务多于可用的 cpus/线程。正如此处的文档中所述, “池中的工作进程通常在池的工作队列的整个持续时间内存在”,并且在我的 Mac 笔记本电脑(4 cpu)的情况下,这会导致一些工作进程接收多个任务,添加QueueHandler
handler
每次都是新的根记录器。如果您正在使用multiprocesing.Pool
,则可以使用该maxtasksperchild=1
参数解决此问题,但此功能似乎不适用于concurrent.futures.process.ProcessPoolExecutor
. 我看到差异的原因是多重的QueueHandler
macOS 和 Ubuntu 之间的处理程序(EDIT#1)是我的 Ubuntu 机器有 128 个线程,因此对get_queue_logger()
where 的 10 个调用中的每一个都被分配给不同的进程。macOS 仍然为记录器报告不同(但不再重复)的 id,因为它默认使用“spawn”而不是“fork”;如果multiprocessing.set_start_method('fork')
与 一起设置multiprocesing.Pool(maxtasksperchild=1)
,则在所有情况下都将获得相同的记录器 ID。
推荐阅读
- iis-8 - URL 重写规则:HTTP 到 HTTPS 在 IIS 8 2016 服务器中不起作用:ERR_INVALID_REDIRECT
- java - 如何在 javaFX 中创建 textAreas
- c++ - 为什么我的程序中的第二个while循环在它之前有一个while循环时不起作用?
- android - 如何从 GridView 中删除内部滚动?
- python-3.x - 如何将日期传递给熊猫 date_range
- python - 当 pytest 运行一个目录中的所有测试时,它如何决定最后运行哪个测试?
- css - 以百分比表示的 flex-basis 值如何工作?
- css - 在woocomerce上的“添加到购物车”按钮之前添加图像
- php - 使用 Laravel、Ajax 和 jQuery 的“点赞”系统
- system-verilog - 在可综合 SystemVerilog 的枚举 typedef 中使用 don't-care