繁体   English   中英

使用Python的多处理程序时如何继承父记录器? 特别是对于paramiko

[英]How can I inherit parent logger when using Python's multiprocessing? Especially for paramiko

我正在使用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)

但是,当我将main函数更改为以下代码来控制时间时(将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不再打印日志。 现在,我要将日志记录配置设置为与父配置相同,该怎么办?

我不希望有一个答案告诉我再次获取记录器,因为在生产服务器中有一个全局记录设置,并且可能会不时地更改,因此我无法配置自己的记录设置,该设置不受全局设置。

所以我想知道是否有一种方法可以将子进程的日志记录设置配置为父级。

在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避免这种get_context

from multiprocessing import get_context

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

建议:

  1. 使用get_context创建一个新的Pool并在该Pool中使用process来为您完成工作。

  2. 池中的每个进程都可以访问父进程的日志配置。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM