【问题标题】:Python: redirect ONLY shared library (C++) stdout to filePython:仅将共享库(C++)标准输出重定向到文件
【发布时间】:2021-03-29 17:06:13
【问题描述】:

我正在使用一个 Python 库(用 C++ 编写),它在控制台中输出大量消息。不幸的是,该库是封闭源代码,因此无法更改其行为。我的目标是编写一个 Python 上下文处理程序,它将库 C++ 输出(标准输出和标准错误)重定向到两个文件中,同时保持 Python 输出不变(显示在控制台中)。到目前为止,这是我设法完成的:

import os
import sys
from pathlib import Path

log_dir = Path('./')

class logSaver():
    def __init__(self, logname):
        self.logname = logname
        sys.stdout.flush()
        sys.stderr.flush()

        if self.logname == None:
            self.logpath_out = os.devnull
            self.logpath_err = os.devnull
        else:
            self.logpath_out = log_dir / (logname + "_out.log")
            self.logpath_err = log_dir / (logname + "_err.log")

        self.logfile_out = os.open(self.logpath_out, os.O_WRONLY|os.O_TRUNC|os.O_CREAT)
        self.logfile_err = os.open(self.logpath_err, os.O_WRONLY|os.O_TRUNC|os.O_CREAT)
    
    def __enter__(self):
        self.orig_stdout = sys.stdout # save original stdout
        self.orig_stderr = sys.stderr # save original stderr

        self.new_stdout = os.dup(1)
        self.new_stderr = os.dup(2)

        os.dup2(self.logfile_out, 1)
        os.dup2(self.logfile_err, 2)

        sys.stdout = os.fdopen(self.new_stdout, 'w')
        sys.stderr = os.fdopen(self.new_stderr, 'w')
        
    def __exit__(self, exc_type, exc_val, exc_tb):
        sys.stdout.flush()
        sys.stderr.flush()

        sys.stdout = self.orig_stdout # restore original stdout
        sys.stderr = self.orig_stderr # restore original stderr
        
        os.close(self.logfile_out)
        os.close(self.logfile_err)

然后按如下方式使用上下文处理程序:

with logSaver("log_filename"):
    some_cpp_and_python_code()

结果是创建了两个文件:

  • log_filename_out.log
  • log_filename_err.log

包含 C++ 库产生的所有输出,而 Python 产生的输出会定期显示在控制台中,不会写入日志文件。

代码工作正常,只重定向 C++ 标准输出,而 Python print() 调用保持不变,但是,由于某种原因,它只工作一次:上下文处理程序的所有后续使用都会导致Python print() 调用,因为它们没有显示在任何地方(控制台或日志文件)。我怀疑问题可能在于原始stdout和stderr的恢复不正确。

如何更改代码以解决此问题?

非常感谢您

【问题讨论】:

  • 我认为您需要在退出处理程序中再次调用 dup2,而不是简单地更改数字描述符。
  • @SergeyA 你的意思是os.dup2(sys.stdout, self.orig_stdout)?
  • @SergeyA 使用os.dup2(self.orig_stdout, 1) 解决了我的问题!谢谢谢尔盖!

标签: python shared-libraries stdout stderr


【解决方案1】:

感谢@SergeyA 的评论,我设法解决了这个问题。以下是可能感兴趣的人的最终工作代码:


class logSaver():
    def __init__(self, logname):
        self.logname = logname
        sys.stdout.flush()
        sys.stderr.flush()
        if self.logname == None:
            self.logpath_out = os.devnull
            self.logpath_err = os.devnull
        else:
            self.logpath_out = logname + "_out.log"
            self.logpath_err = logname + "_err.log"
        self.logfile_out = os.open(self.logpath_out, os.O_WRONLY|os.O_TRUNC|os.O_CREAT)
        self.logfile_err = os.open(self.logpath_err, os.O_WRONLY|os.O_TRUNC|os.O_CREAT)
    
    def __enter__(self):
        self.orig_stdout = os.dup(1)
        self.orig_stderr = os.dup(2)
        self.new_stdout = os.dup(1)
        self.new_stderr = os.dup(2)
        os.dup2(self.logfile_out, 1)
        os.dup2(self.logfile_err, 2)
        sys.stdout = os.fdopen(self.new_stdout, 'w')
        sys.stderr = os.fdopen(self.new_stderr, 'w')
        
    def __exit__(self, exc_type, exc_val, exc_tb):
        sys.stdout.flush()
        sys.stderr.flush()

        os.dup2(self.orig_stdout, 1)
        os.dup2(self.orig_stderr, 2)
        os.close(self.orig_stdout)
        os.close(self.orig_stderr)
        
        os.close(self.logfile_out)
        os.close(self.logfile_err)

问题是我正在恢复原始流

sys.stdout = self.orig_stdout

正确的做法是:

os.dup2(self.orig_stdout, 1)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-05-23
    • 1970-01-01
    • 1970-01-01
    • 2016-06-26
    • 1970-01-01
    相关资源
    最近更新 更多