Почему переопределение sys.stdout не работает для вывода из многопроцессорной обработки. Процесс? - PullRequest
0 голосов
/ 21 октября 2018

Я перенаправляю стандартный вывод в логгер, и теперь я порождаю процесс, используя многопроцессорность. Процесс.Однако, несмотря на то, что стандартный вывод процессов перенаправлен на родительский стандартный вывод, он игнорирует переопределение sys.stdout.Вот пример:

import multiprocessing
import sys
import logging

def worker():
    print('Hello from the multiprocessing')
    sys.stdout.flush()

class LoggerWriter:
    def __init__(self, logger, level):
        self.logger = logger
        self.level = level

    def write(self, message):
        if message != '\n':
            self.logger.log(self.level, "LOGGER: "+message)

    def flush(self):
        pass


if __name__ == '__main__':
    logging.basicConfig(level=logging.INFO, format='%(message)s')
    sys.stdout = LoggerWriter(logging.getLogger(), logging.INFO)
    p = multiprocessing.Process(target=worker)
    print("Hello from main")
    p.start()
    p.join()

Я ожидал, что он напечатает

LOGGER: Hello from main
LOGGER: Hello from the multiprocessing

, но вместо этого я получаю

LOGGER: Hello from main
Hello from the multiprocessing

Он полностью игнорирует sys.stdout ... Это почему?Можно ли достичь первого случая?

Примечание. Это в Windows 7 - похоже, это может сыграть свою роль.

Ответы [ 2 ]

0 голосов
/ 21 октября 2018

Основываясь на комментариях и ответе @ user2357112, в конечном итоге я использовал потоки. Поток для обработки журналов дочернего процесса (обмениваемого через очередь), при этом все еще используя Process для реальной работы.Здесь на тот случай, если кому-то понадобится нечто подобное.

В основном после добавления:

class LoggedProcess(multiprocessing.Process):

    class LoggerWriter:
        def __init__(self, queue):
            self.queue = queue

        def write(self, message):
            for line in message.rstrip().splitlines():
                self.queue.put(line.rstrip())

        def flush(self):
            pass

    @staticmethod
    def logged_worker(logger_queue, worker, *args, **kwargs):
        import sys
        sys.stdout = sys.stderr = LoggedProcess.LoggerWriter(logger_queue)
        logging.basicConfig(format="%(message)s", level=logging.INFO)
        try:
            worker(*args, **kwargs)
        except:
            pass
        logger_queue.put(None)

    @staticmethod
    def process_logger(process, logger_queue, name):
        while True:
            try:
                if not process.is_alive():
                    raise EOFError()
                msg = logger_queue.get(timeout=1)
                if msg is None:
                    raise EOFError()
                logging.getLogger().log(logging.INFO, f"[PROCESS {process.pid} {name}] {msg}")
            except queue.Empty:
                pass # timeout
            except Exception:
                break # queue closed

    def __init__(self, target, log_name='', args=(), kwargs={}):
        self.logger_queue = multiprocessing.Queue()
        self.log_name = log_name
        super().__init__(target=self.logged_worker, args=(self.logger_queue, target, *args), kwargs=kwargs)


    def start(self):
        super().start()
        logger_t = threading.Thread(target=self.process_logger, args=(self, self.logger_queue, self.log_name))
        logger_t.setDaemon(True)
        logger_t.start()

    def terminate(self):
        super().terminate()
        super().join()
        self.logger_queue.put(None)

мы можем просто заменить p = multiprocessing.Process(target=worker) на p = LoggedProcess(target=worker).Теперь журналы дочерних процессов смешиваются с регистратором родительских процессов, в этом случае получается

LOGGER: Hello from main
[PROCESS 5000 ] Hello from the multiprocessing

Я добавил некоторую дополнительную информацию о процессе, но первоначальное намерение все еще может быть выполнено, плюс теперь оно может быть дляНапример, все они помещаются в один и тот же файл журнала родительским процессом, или все, что нужно.

0 голосов
/ 21 октября 2018

Вы работаете в Windows, поэтому вы используете метод spawn для запуска многопроцессорных рабочих.Этот метод запускает новый интерпретатор Python с нуля и импортирует ваш скрипт как модуль перед началом работы.

Поскольку ваши рабочие запускаются с нуля, а не разветвляются, они не наследуют автоматически настройки, выполняемые основным процессом,включая установку, как вашу оболочку stdout, и они не переделывают эту настройку, потому что она находится внутри if __name__ == '__main__' защиты.У них просто есть обычные sys.stdout.

. Вы должны будете договориться с рабочими, чтобы они настраивали свои собственные обертки stdout, возможно, разместив настройку обертки за пределами if __name__ == '__main__' guard.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...