You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将subprocess调用程序的标准输出重定向到RotatingFileHandler处理的滚动日志文件?

如何将subprocess调用程序的标准输出重定向到RotatingFileHandler处理的滚动日志文件?

问题分析

你当前的代码无法触发日志滚动的核心原因是:子进程直接绕过了Python logging模块的逻辑,往底层文件描述符写数据。

你通过logger.fileno = logger.handlers[0].stream.fileno把handler的文件描述符暴露给subprocess,相当于让子进程直接往test.log文件写内容——这个操作完全脱离了logging模块的管控。RotatingFileHandler的滚动逻辑只有在通过logger.info()/logger.debug()等日志接口写入时才会触发(此时logging会检查文件大小并判断是否需要滚动),而子进程直接写文件的行为,logging模块根本感知不到,自然不会执行滚动操作。

解决方案:让子进程输出经过logging模块处理

正确的做法是让subprocess把输出写到管道,然后在Python代码中捕获输出内容,再通过logging接口将内容写入日志。这样就能完全利用RotatingFileHandler的滚动机制了。

方案1:一次性捕获输出后写入日志(适合输出量不大的场景)

import subprocess as subp
import logging
from logging.handlers import RotatingFileHandler

# 配置滚动日志
logger = logging.getLogger('My rolling log')
logger.setLevel(logging.INFO)  # 必须设置日志级别,默认是WARNING,低于级别的日志不会输出
handler = RotatingFileHandler(
    'test.log', 
    maxBytes=1000,
    backupCount=1, 
    mode='a',  # 改用追加模式,避免每次启动清空日志(如果需要清空可以在初始化前删除文件)
    encoding='latin-1'
)
logger.addHandler(handler)

# 启动子进程,将stdout/stderr指向管道
s = subp.Popen(
    'mycode.x', 
    stdin=subp.PIPE, 
    stdout=subp.PIPE, 
    stderr=subp.PIPE,
    text=False
)

# 发送输入并获取所有输出
sortie, err = s.communicate(input=cmd.encode())

# 将子进程stdout内容写入滚动日志
if sortie:
    # 按行分割输出,逐行写入(也可以直接用logger.info(sortie.decode('latin-1')))
    for line in sortie.decode('latin-1').splitlines():
        logger.info(line)

# 处理stderr内容(可选)
if err:
    for line in err.decode('latin-1').splitlines():
        logger.error(line)

方案2:逐行捕获输出并写入日志(适合大输出量场景,避免内存占用过高)

如果子进程输出非常大,一次性读取所有内容会占用过多内存,可以改用逐行读取的方式:

import subprocess as subp
import logging
from logging.handlers import RotatingFileHandler

# 配置滚动日志(同方案1)
logger = logging.getLogger('My rolling log')
logger.setLevel(logging.INFO)
handler = RotatingFileHandler(
    'test.log', 
    maxBytes=1000,
    backupCount=1, 
    mode='a', 
    encoding='latin-1'
)
logger.addHandler(handler)

# 启动子进程
s = subp.Popen(
    'mycode.x', 
    stdin=subp.PIPE, 
    stdout=subp.PIPE, 
    stderr=subp.PIPE,
    text=False
)

# 发送输入并关闭stdin
s.stdin.write(cmd.encode())
s.stdin.close()

# 逐行读取stdout并写入日志
for line in iter(s.stdout.readline, b''):
    if line:
        logger.info(line.decode('latin-1').rstrip('\n'))

# 读取并处理stderr
err = s.stderr.read()
s.wait()
if err:
    for line in err.decode('latin-1').splitlines():
        logger.error(line)

额外注意事项

  1. 日志级别:必须设置logger.setLevel(logging.INFO),否则默认的WARNING级别会过滤掉INFO级别的日志。
  2. 文件模式:原代码中的mode='w+'会每次启动程序时清空日志文件,改用mode='a'更符合滚动日志的追加场景;如果确实需要每次启动清空,可以在初始化handler前执行import os; os.remove('test.log') if os.path.exists('test.log') else None。
  3. 编码一致性:确保子进程输出的编码和logging handler的编码(latin-1)一致,避免乱码。

备注:内容来源于stack exchange,提问作者narsonalin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.14 09:03:00