Как я могу наследовать родительский логгер при использовании многопроцессорной обработки Python?Специально для парамико - PullRequest
0 голосов
/ 20 ноября 2018

Я использую многопроцессорность Python.Я установил регистратор в родительском процессе, но я не могу просто унаследовать настройки журналирования родителя.

Я не беспокоюсь о смешивании журнала, поскольку я использую многопроцессорность не для одновременного выполнения заданий, а длядля контроля времени, поэтому одновременно выполняется только один подпроцесс.

Мой код без многопроцессорной обработки:

from multiprocessing import Process
import paramiko
import logging
import sys


def sftp_read():
    # log.debug("Child process started")  # This line will cause exception if it is run in sub process.
    ssh = paramiko.SSHClient()
    ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    timeout = 60
    ssh.connect('my_server', username='my_user', password='my_password', timeout=timeout, auth_timeout=timeout,
                banner_timeout=timeout)
    sftp = ssh.open_sftp()
    fp = sftp.file('/home/my_user/my_file.txt')
    lines = fp.readlines()
    print ''.join(lines)
    fp.close()
    ssh.close()


def main():
    sftp_read()  # Call this function without multiprocessing

if __name__ == '__main__':
    logging.basicConfig(stream=sys.stdout,
                        format='[%(asctime)s] {%(filename)s:%(lineno)d} %(levelname)s - %(message)s')
    log = logging.getLogger()
    log.setLevel(logging.DEBUG)
    main()

Приведенный выше код работает правильно, paramiko печатает журнал, как показано ниже:

[2018-11-20 10:38:45,051] {transport.py:1746} DEBUG - starting thread (client mode): 0x3052208L
[2018-11-20 10:38:45,051] {transport.py:1746} DEBUG - Local version/idstring: SSH-2.0-paramiko_2.4.2
[2018-11-20 10:38:45,405] {transport.py:1746} DEBUG - Remote version/idstring: SSH-2.0-OpenSSH_7.2p2 Ubuntu-4ubuntu2.6
[2018-11-20 10:38:45,405] {transport.py:1746} INFO - Connected (version 2.0, client OpenSSH_7.2p2)

Но когда я изменяю основную функцию на следующий код для управления временем (ограничьте максимальное время чтения SFTP до 15 секунд):

def main():
    # Use multiprocessing to limit the running time to at most 15 seconds.
    p = Process(target=sftp_read)
    try:
        log.debug("About to start SSH")
        p.start()
        log.debug('Process started')
        p.join(15)
    finally:
        if p.is_alive():
            p.terminate()
            log.debug('Terminated')
        else:
            log.debug("Finished normally")

Paramiko больше не печатаетжурнал.Теперь я хочу установить конфигурацию ведения журнала так же, как родительскую конфигурацию, как я могу это сделать?

Я не хочу, чтобы ответ велел мне снова получить регистратор, потому что на моем рабочем сервере естьглобальный параметр ведения журнала и может время от времени меняться, я не могу настроить свой собственный параметр ведения журнала, который не контролируется глобальным параметром.

Поэтому мне интересно, есть ли способ, позволяющий мне настроить мойНастройка ведения журнала подпроцесса как родительского.

1 Ответ

0 голосов
/ 20 ноября 2018

В Python подпроцессы запускаются по стандарту POSIX.Подпроцессы в стандарте POSIX создаются с использованием системного вызова fork.Дочерний процесс, созданный с использованием fork, по сути является копией всего, что находится в памяти родительского процесса.В вашем случае дочерний процесс будет иметь доступ к логгеру от родителя.

Предупреждение: fork копирует все;но не копирует threads.Все потоки, работающие в родительском процессе, не существуют в дочернем процессе.

import logging
from multiprocessing import Pool
from os import getpid

def runs_in_subprocess():
    logging.info(
        "I am the child, with PID {}".format(getpid()))

if __name__ == '__main__':
    logging.basicConfig(
        format='GADZOOKS %(message)s', level=logging.DEBUG)

    logging.info(
        "I am the parent, with PID {}".format(getpid()))

    with Pool() as pool:
        pool.apply(runs_in_subprocess)

Вывод:

GADZOOKS I am the parent, with PID 3884
GADZOOKS I am the child, with PID 3885

Обратите внимание, как дочерние процессы в вашем пуле наследуют конфигурацию ведения журнала родительского процесса

Вы можете столкнуться с проблемой deadlocks, поэтому остерегайтесь следующего:

  1. Всякий раз, когда поток в родительском процессе записывает сообщения журнала, он добавляет его в очередь.Это включает в себя получение блокировки.

  2. Если fork () происходит не в то время, блокировка копируется в полученное состояние.

  3. Дочерний процесс копирует конфигурацию ведения журнала родителя, включая очередь.Всякий раз, когда дочерний процесс записывает сообщение журнала, он пытается записать его в очередь.

  4. Это означает получение блокировки, но блокировка уже получена.

  5. Дочерний процесс теперь ожидает снятия блокировки.

  6. Блокировка никогда не будет снята, поскольку поток, который освободит ее, не был скопированfork ().

В python3 этого можно избежать, используя get_context.

from multiprocessing import get_context

def your_func():
    with get_context("spawn").Pool() as pool:
        # ... everything else is unchanged

Совет:

  1. Используя get_context создайте новый Пул и используйте процесс 'в этом пуле, чтобы выполнить работу за вас.

  2. Каждый процесс из пула будет иметь доступ к родительскому процессу' log config.

...