Redirect stdout to log file when printing to stdout?

I would like to capture the console output at the end of a Python script. That is, I want to both print to the console as usual, and at the end of execution save the console output to a file.

I've seen various related SO questions 1 , 2 , 3 , although they either just redirect the output, don't display it, or use it logging

. From what I can tell by reading the logging

doc , you can only write the output from the code you wrote.

The problem with all the links above is the console output from code you haven't written yet, which is also output to the console. I want all console output of the program to be at the end of execution.

My first instinct was something like

logFile = open('LogFile.txt', 'w')
def print_log(msg):
    print(msg)
    logFile.write(msg)

print_log('Hello World!')

logFile.close()

      

But that still won't be able to capture the console output from other code and libraries in use. Is there a way to save the console output of a python script at the end of execution? I feel like there should be an easy way to do this, but all my research has not led to a consistent solution.

+3


source to share


1 answer


I used this in one of my projects:

import io
import sys
from enum import Enum


class Tee(io.StringIO):
    class Source(Enum):
        STDOUT = 1
        STDERR = 2

    def __init__(self, clone=Source.STDOUT, *args, **kwargs):
        super().__init__(*args, **kwargs)

        self._clone = clone

        if clone == Tee.Source.STDOUT:
            self._out = sys.stdout
        elif clone == Tee.Source.STDERR:
            self._out = sys.stderr
        else:
            raise ValueError("Clone has to be STDOUT or STDERR.")

    def write(self, *args, **kwargs):
        self._out.write(*args, **kwargs)
        return super().write(*args, **kwargs)

    def __enter__(self):
        if self._clone == Tee.Source.STDOUT:
            sys.stdout = self
        else:
            sys.stderr = self
        self.seek(io.SEEK_END)
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        if self._clone == Tee.Source.STDOUT:
            sys.stdout = self._out
        else:
            sys.stderr = self._out
        self.seek(0)
        return False

      

Basically this is exactly what Maxim Markov said in the comment, with one difference. I usually don't want to stop any exits, so I wrote this Tee

one that captures all text going to stdout (or stderr), prints it out right away and stores it in a buffer for later use. It also takes care of the "commit" sys.stdout

when the code exits the block with

.



Usage example:

if __name__ == "__main__":
    with Tee() as tee:
        print("Hello World!")

    print(tee.read())

      

There are some drawbacks, like no extra code that you can't use tee.read()

inside a block. But in my case, I always need to process the output of the entire block.

+1


source







All Articles