簡體   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