简体   繁体   English

Python:仅将共享库 (C++) 标准输出重定向到文件

[英]Python: redirect ONLY shared library (C++) stdout to file

I am using a Python library (written in C++) that outputs a lot of messages in the console.我正在使用在控制台中输出大量消息的 Python 库(用 C++ 编写)。 Unfortunately the library is closed source, so there is no way to change its behavior.不幸的是,该库是封闭源代码,因此无法更改其行为。 My goal is to write a Python context handler that redirects the library C++ output (both stdout ans stderr) into two files, while leaving the Python output unchanged (shown in the console). My goal is to write a Python context handler that redirects the library C++ output (both stdout ans stderr) into two files, while leaving the Python output unchanged (shown in the console). This is what I managed to accomplish so far:到目前为止,这是我设法完成的:

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)

The context handler is then used as follows:然后按如下方式使用上下文处理程序:

with logSaver("log_filename"):
    some_cpp_and_python_code()

The result is the creation of two files:结果是创建了两个文件:

  • log_filename_out.log log_filename_out.log
  • log_filename_err.log log_filename_err.log

that contain all the output produced by the C++ library, while the output produced by Python is shown regularly into the console, and is not written into the log files. that contain all the output produced by the C++ library, while the output produced by Python is shown regularly into the console, and is not written into the log files.

The code works fine, and redirects only the C++ stdout while leaving unchanged the Python print() calls, however, for some reason, it works only one time: all subsequent uses of the context handler result in the broken behaviour of the Python print() calls, as they are not shown anywhere (console or log files).该代码工作正常,并且只重定向 C++ 标准输出,同时保持 Python print() print()调用不变,但是,由于某种原因,它只工作一次:上下文处理程序的所有后续使用都会导致 ZA7F5117F35426B563824 的损坏行为print()调用,因为它们没有显示在任何地方(控制台或日志文件)。 I suspect the issue might lie in the incorrect restoration of the original stdout and stderr.我怀疑问题可能在于原始 stdout 和 stderr 的恢复不正确。

How can the code be changed to fix this behaviour?如何更改代码以修复此行为?

Thank you very much in advance非常感谢您提前

Thanks to @SergeyA comment, I managed to fix the problem.感谢@SergeyA 的评论,我设法解决了这个问题。 Here is the final working code for whoever might be interested:这是可能感兴趣的人的最终工作代码:


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)

The problem was that I was restoring the original streams with问题是我正在恢复原始流

sys.stdout = self.orig_stdout

while the correct way was to do:而正确的方法是:

os.dup2(self.orig_stdout, 1)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM