Как предотвратить наследование регистраторов и обработчиков python во время многопроцессорной обработки на основе fork?
предположим, я настроил обработчики журналов в основном процессе. Основной процесс порождает некоторых детей и из-за 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
.
как предотвратить наследование регистраторов и обработчиков без их удаления или удаления в исходном Родительском процессе?
EDIT: было бы хорошей идеей просто удалить все обработчики при инициализации пула?
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()
все (файловые) обработчики во время функция инициализации?
3 ответов
вам не нужно предотвращать это, вам просто нужно перенастроить иерархию ведения журнала.
Я думаю, что вы на правильном пути с инициализатором пула. Но вместо того, чтобы пытаться взломать вещи, пусть пакет ведения журнала делает то, для чего он предназначен. Пусть пакет ведения журнала выполняет перенастройку иерархии ведения журнала в рабочих процессах.
вот пример:
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 основного процесса.
самый простой ответ заключается в том, что вам, вероятно, следует избегать изменения глобалов с помощью multiprocessing
. Обратите внимание, что корневой регистратор, который вы получаете с помощью logging.getLogger()
, является глобальной.
самый простой способ обойти это, просто создавая новый logging.Logger
экземпляра для каждого процесса. Вы можете назвать их после процессов, или просто случайным образом:
log= logging.getLogger(str(uuid.uuid4()))
вы также можете проверить как мне войти при использовании многопроцессорной обработки в python
Если вы нужно чтобы предотвратить наследование иерархии ведения журнала в рабочих процессах, просто выполните конфигурацию ведения журнала после создание пула работников. Из вашего примера:
pool = mp.Pool(n_cores)
logging.basicConfig(format=format, level=logging.INFO)
тогда ничего не будет унаследовано.
в противном случае, как вы сказали, из-за ОС.fork(), вещи будут унаследованы / дублированы. В этом случае ваши параметры перенастраивают ведение журнала после создания пула (см. Мой другой ответ) или другие (ы) предложения/ответы.