Python 如何将非阻塞的stderr捕获添加到线程化popen';s

Python 如何将非阻塞的stderr捕获添加到线程化popen';s,python,multithreading,subprocess,blocking,stderr,Python,Multithreading,Subprocess,Blocking,Stderr,我有一个用于备份和加密mysqldump文件的python3脚本,我对一个加密和压缩后67gb的数据库有一个特殊的问题。 mysqldump正在输出错误代码3,因此我想捕获实际的错误消息,因为这可能意味着一些事情。 随机情况是备份文件大小正确,因此不确定错误的含义。它在这个数据库上工作过一次 代码如下所示,如果p1和p2的返回代码都不是0,我非常感谢您提供一些帮助,帮助我添加stderr的非阻塞捕获 此外,如果我做了任何明显错误的事情,请务必让我知道,因为我想确保这是一个可靠的过程。在我的数据库

我有一个用于备份和加密mysqldump文件的python3脚本,我对一个加密和压缩后67gb的数据库有一个特殊的问题。 mysqldump正在输出错误代码3,因此我想捕获实际的错误消息,因为这可能意味着一些事情。 随机情况是备份文件大小正确,因此不确定错误的含义。它在这个数据库上工作过一次

代码如下所示,如果p1和p2的返回代码都不是0,我非常感谢您提供一些帮助,帮助我添加stderr的非阻塞捕获

此外,如果我做了任何明显错误的事情,请务必让我知道,因为我想确保这是一个可靠的过程。在我的数据库上,它在15gb压缩下运行良好

def dbbackup():
    while True:
        item = q.get()
        #build up folder structure, daily, weekly, monthy & project
        genfile = config[item]['DBName'] + '-' + dateyymmdd + '-'
        genfile += config[item]['PubKey'] + '.sql.gpg'
        if os.path.isfile(genfile):
            syslog.syslog(item + ' ' + genfile + ' exists, removing')
            os.remove(genfile)
        syslog.syslog(item + ' will be backed up as ' + genfile)
        args = ['mysqldump', '-u', config[item]['UserNm'],
                '-p' + config[item]['Passwd'], '-P', config[item]['Portnu'],
                '-h', config[item]['Server']]
        args.extend(config[item]['MyParm'].split())
        args.append(config[item]['DBName'])
        p1 = subprocess.Popen(args, stdout=subprocess.PIPE)
        p2 = subprocess.Popen(['gpg', '-o', genfile, '-r',
                               config[item]['PubKey'], '-z', '9', '--encrypt'], stdin=p1.stdout)
        p2.wait()
        if p2.returncode == 0:
            syslog.syslog(item + ' encryption successful')
        else:
            syslog.syslog(syslog.LOG_CRIT, item + ' encryption failed '+str(p2.returncode))
            p1.terminate()
        p1.wait()
        if p1.returncode == 0:
        #does some uploads of the file etc..
        else:
            syslog.syslog(syslog.LOG_CRIT, item + ' extract failed '+str(p1.returncode))
        q.task_done()


def main():
    db2backup = []
    for settingtest in config:
            db2backup.append(settingtest)
    if len(db2backup) >= 1:
        syslog.syslog('Backups started')
        for database in db2backup:
            q.put(database)
            syslog.syslog(database + ' added to backup queue')
        q.join()
        syslog.syslog('Backups finished')


q = queue.Queue()
config = configparser.ConfigParser()
config.read('backup.cfg')
backuptype = 'daily'
dateyymmdd = datetime.datetime.now().strftime('%Y%m%d')


for i in range(2):
    t = threading.Thread(target=dbbackup)
    t.daemon = True
    t.start()

if __name__ == '__main__':
    main()
简化您的代码:

  • 避免不必要的全局变量,而是将参数传递给相应的函数
  • 避免重新实现线程池(这会损害可读性,并错过多年积累的便利功能)
捕获stderr的最简单方法是使用
stderr=PIPE
.communicate()
(阻塞调用):

注意:如果
gpg
过早死亡,则无需调用
db\u dump.terminate()
mysqldump
在尝试向关闭的
gpg.stdin
写入内容时死亡

如果配置中有大量项,则可以使用
pool.imap()
而不是
pool.starmap()
()


为保证健壮性,请使用wrap
backup\u db()
函数捕获并记录所有异常。

为什么要在p2之后等待p1,而且p1.stdout也应该关闭。我也看不到您在任何地方重定向stderr。p1.wait用于确保p1.returncode未被阻止或导致错误。我还没有添加stderr,因为我无法正确地添加它,所以希望有人能提供建议。启动p2之后的p1.stdout.close()调用对于p1接收信号管道(如果p2在p1之前退出)非常重要。[-im采用顶部答案中使用的方法,等待应该结束,但有兴趣从您从python文档中复制的那一行中了解这一点。相关:
#!/usr/bin/env python3
from configparser import ConfigParser
from datetime import datetime
from multiprocessing.dummy import Pool
from subprocess import Popen, PIPE

def backup_db(item, conf): # config[item] == conf
    """Run `mysqldump ... | gpg ...` command."""
    genfile = '{conf[DBName]}-{now:%Y%m%d}-{conf[PubKey]}.sql.gpg'.format(
                conf=conf, now=datetime.now())
    # ...
    args = ['mysqldump', '-u', conf['UserNm'], ...]
    with Popen(['gpg', ...], stdin=PIPE) as gpg, \
         Popen(args, stdout=gpg.stdin, stderr=PIPE) as db_dump:
        gpg.communicate() 
        error = db_dump.communicate()[1]
    if gpg.returncode or db_dump.returncode:
        error

def main():
    config = ConfigParser()
    with open('backup.cfg') as file: # raise exception if config is unavailable
        config.read_file(file)
    with Pool(2) as pool:
        pool.starmap(backup_db, config.items())

if __name__ == "__main__":
    main()