如何用subprocess.check_output()实现子进程实时日志与自定义错误捕获?
问题分析
你当前的代码实现了main.py日志实时打印到控制台,但无法捕获其自定义错误信息,核心原因是:
- 使用
subprocess.check_output(..., stderr=sys.stderr)时,子进程的stderr被直接重定向到父进程的stderr(控制台),而非捕获到子进程的管道中,因此CalledProcessError的e.stderr属性为None,无法获取自定义错误内容。
优化方案
要同时满足实时打印日志和捕获自定义错误两个需求,需改用subprocess.Popen手动处理子进程的输出流,既实时打印到控制台,又将stderr内容保存用于错误返回。
修改后的app.py代码
import sys import subprocess from pathlib import Path from flask import app @app.route('/run_script', methods=['POST']) def run_script_now(): main_path = str(Path("main.py").resolve()) error_output = [] try: # 启动子进程,开启stdout/stderr管道,启用行缓冲实现实时输出 with subprocess.Popen( ["python", main_path], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, bufsize=1, universal_newlines=True ) as proc: # 实时打印stdout到控制台 for line in proc.stdout: print(line.rstrip(), file=sys.stdout) sys.stdout.flush() # 实时打印stderr到控制台,同时保存错误内容 for line in proc.stderr: print(line.rstrip(), file=sys.stderr) sys.stderr.flush() error_output.append(line.rstrip()) # 等待子进程结束,检查返回码 returncode = proc.wait() if returncode != 0: # 手动抛出异常并传入捕获的stderr内容 raise subprocess.CalledProcessError( returncode, proc.args, stderr='\n'.join(error_output) ) msg = ("Success", 200) logger.info(msg) return msg except subprocess.CalledProcessError as e: error_msg = f"Error: {e.stderr}" logger.critical(error_msg) return error_msg, 500
main.py补充优化
确保自定义错误信息能被正确输出,在sys.stderr.write后添加换行并刷新缓冲区:
try: portfolio_summary = processing.process_portfolio_summary() except Exception as ex: error_msg = str(ex) sys_msg = f"Error en PortfolioSummary: {error_msg}" logger.error(sys_msg) # 添加换行+刷新缓冲区,避免内容滞留 sys.stderr.write(f"{sys_msg}\n") sys.stderr.flush() sys.exit(1)
关键说明
Popen替代check_output:允许手动控制输出流,兼顾实时打印和内容捕获。- 行缓冲设置:
bufsize=1确保子进程输出逐行刷新,实现实时日志效果。 - 双路处理stderr:既打印到控制台,又将每一行错误内容存入列表,最后拼接成完整错误信息传入异常。
- 手动抛出异常:子进程返回非0码时,主动构造
CalledProcessError并传入捕获的stderr,保证e.stderr能拿到自定义错误。
内容的提问来源于stack exchange,提问作者Diego
相关产品推荐
相关产品推荐

