Python如何将subprocess的stderr输出重定向到日志文件
问题场景
我正在使用subprocess在一个Python脚本中调用另一个Python脚本,两个脚本需要运行在各自的Python版本下,分别命名为main.py和called_from_main.py。其中main.py中使用logging模块将日志输出重定向到了本地文件。
main.py 原有代码
try: returned = subprocess.check_output(r'"C:\path\to\python.exe" "C:\path\to\called_from_main.py"') logger.info(returned) except subprocess.CalledProcessError as error: logger.exception(error) raise RuntimeError("That did not work")
called_from_main.py 代码
# 简化示例,当前需要主动抛出错误 raise Exception("Error occured in called_from_main")
运行代码后,输出日志中仅能看到ERROR: Command '"C:\path\to\python.exe" "C:\path\to\called_from_main.py"' returned non-zero exit status 1.,但看不到Error occured in called_from_main这条具体错误信息。需要将subprocess的stderr输出重定向到已配置为输出到文件的logger中,该如何实现?
解决方法
原因说明
subprocess.check_output默认仅捕获子进程的标准输出(stdout),子进程的标准错误(stderr)默认会直接输出到父进程的stderr流,不会存入CalledProcessError对象的属性中,因此你无法从现有日志中拿到子进程的具体报错内容。
方案1:合并标准输出和标准错误
在调用check_output时添加stderr=subprocess.STDOUT参数,将子进程的stderr合并到stdout中,同时指定编码直接返回字符串,避免日志出现bytes格式乱码:
修改后的main.py代码如下:
import subprocess try: returned = subprocess.check_output( r'"C:\path\to\python.exe" "C:\path\to\called_from_main.py"', stderr=subprocess.STDOUT, # 合并标准错误到标准输出 encoding='utf-8', errors='replace' ) logger.info(returned) except subprocess.CalledProcessError as error: # error.output已包含子进程的所有输出信息,包含报错内容 logger.error(f"子进程执行失败,错误详情:{error.output}", exc_info=True) raise RuntimeError("脚本调用失败")
方案2:单独捕获stderr
如果需要分开处理标准输出和标准错误,可改用subprocess.run方法分别获取两个流的内容:
import subprocess result = subprocess.run( r'"C:\path\to\python.exe" "C:\path\to\called_from_main.py"', stdout=subprocess.PIPE, stderr=subprocess.PIPE, encoding='utf-8', errors='replace' ) if result.returncode == 0: logger.info(f"子进程执行成功,输出:{result.stdout}") else: logger.error(f"子进程执行失败,正常输出:{result.stdout},错误信息:{result.stderr}", exc_info=True) raise RuntimeError("脚本调用失败")
两种方案都可以完整将子进程的错误信息写入你已配置的日志文件中,成功捕获到Error occured in called_from_main的具体报错内容。
内容的提问来源于stack exchange,提问作者muliku

