假设我在主进程中配置了日志处理程序。主进程产生一些子进程,由于os.fork()
(在Linux中),所有记录器和处理程序都从主进程继承。在下面的示例中,'Hello World'
将被打印100次到控制台:
import multiprocessing as mp
import logging
def do_log(no):
# root logger logs Hello World to stderr (StreamHandler)
# BUT I DON'T WANT THAT!
logging.getLogger().info('Hello world {}'.format(no))
def main():
format = '%(processName)-10s %(name)s %(levelname)-8s %(message)s'
# This creates a StreamHandler
logging.basicConfig(format=format, level=logging.INFO)
n_cores = 4
pool = mp.Pool(n_cores)
# Log to stdout 100 times concurrently
pool.map(do_log, range(100))
pool.close()
pool.join()
if __name__ == '__main__':
main()
这将打印如下内容:
ForkPoolWorker-1 root INFO Hello world 0
ForkPoolWorker-3 root INFO Hello world 14
ForkPoolWorker-3 root INFO Hello world 15
ForkPoolWorker-3 root INFO Hello world 16
...
但是,我不希望子进程从父进程继承所有日志记录配置。因此,在上面的示例中,do_log
不应向stderr
打印任何内容,因为不应该StreamHandler
。
如何在不删除或删除原始父进程的情况下阻止继承记录器和处理程序?
编辑:在初始化游戏时简单地删除所有处理程序是不是一个好主意?
def init_logging():
for logger in logging.Logger.manager.loggerDict.values():
if hasattr(logger, 'handlers'):
logger.handlers = []
和
pool = mp.Pool(n_cores, initializer=init_logging, initargs=())
此外,我还可以在初始化函数期间安全地close()
所有(文件)处理程序吗?
答案 0 :(得分:4)
您不需要阻止它,您只需要重新配置日志记录层次结构。
我认为您使用游泳池初始化程序处于正确的轨道上。但是,不要试图破解东西,而是让日志包执行其设计的目标。让日志包重新配置工作进程中的日志记录层次结构。
以下是一个例子:
def main():
def configure_logging():
logging_config = {
'formatters': {
'f': {
'format': '%(processName)-10s %(name)s'
' %(levelname)-8s %(message)s',
},
},
'handlers': {
'h': {
'level':'INFO',
'class':'logging.StreamHandler',
'formatter':'f',
},
},
'loggers': {
'': {
'handlers': ['h'],
'level':'INFO',
'propagate': True,
},
},
'version': 1,
}
pname = mp.current_process().name
if pname != 'MainProcess':
logging_config['handlers'] = {
'h': {
'level':'INFO',
'formatter':'f',
'class':'logging.FileHandler',
'filename': pname + '.log',
},
}
logging.config.dictConfig(logging_config)
configure_logging() # MainProcess
def pool_initializer():
configure_logging()
n_cores = 4
pool = mp.Pool(n_cores, initializer=pool_initializer)
pool.map(do_log, range(100))
pool.close()
pool.join()
现在,工作进程将每个都记录到各自的日志文件中,并且将不再使用主进程的stderr StreamHandler。
答案 1 :(得分:2)
最直截了当的答案是你应该避免使用multiprocessing
修改全局变量。请注意,使用logging.getLogger()
获得的根记录器是全局的。
最简单的方法是为每个进程创建一个新的logging.Logger
实例。您可以在流程后命名它们,或者只是随机命名:
log= logging.getLogger(str(uuid.uuid4()))
您可能还想查看 how should I log while using multiprocessing in python
答案 2 :(得分:0)
如果您需要以防止在工作进程中继承日志记录层次结构,只需在创建工作池之后执行日志记录配置。从你的例子:
pool = mp.Pool(n_cores)
logging.basicConfig(format=format, level=logging.INFO)
然后,什么都不会被继承。
否则,就像你说的那样,由于os.fork(),事情会被继承/重复。 在这种情况下,您的选项是在创建池(请参阅我的其他答案)或其他(s)建议/答案后重新配置日志记录。