57
votes

I'm trying to find out a way in python to redirect the script execution log to a file as well as stdout in a pythonic way. Is there any easy way of achieving this?

8
Well, what have you got so far? - Joel Cornett
on the linux console this is script | tee logfilename - Karoly Horvath
Did you mean something like that? [Python output buffering][1] [1]: stackoverflow.com/questions/107705/python-output-buffering - user1065951
Use python logging library!!! its my favorite lib in python :D. - aisbaa

8 Answers

51
votes

I came up with this [untested]

import sys

class Tee(object):
    def __init__(self, *files):
        self.files = files
    def write(self, obj):
        for f in self.files:
            f.write(obj)
            f.flush() # If you want the output to be visible immediately
    def flush(self) :
        for f in self.files:
            f.flush()

f = open('out.txt', 'w')
original = sys.stdout
sys.stdout = Tee(sys.stdout, f)
print "test"  # This will go to stdout and the file out.txt

#use the original
sys.stdout = original
print "This won't appear on file"  # Only on stdout
f.close()

print>>xyz in python will expect a write() function in xyz. You could use your own custom object which has this. Or else, you could also have sys.stdout refer to your object, in which case it will be tee-ed even without >>xyz.

61
votes

Use logging module (http://docs.python.org/library/logging.html):

import logging

logger = logging.getLogger('scope.name')

file_log_handler = logging.FileHandler('logfile.log')
logger.addHandler(file_log_handler)

stderr_log_handler = logging.StreamHandler()
logger.addHandler(stderr_log_handler)

# nice output format
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
file_log_handler.setFormatter(formatter)
stderr_log_handler.setFormatter(formatter)

logger.info('Info message')
logger.error('Error message')
11
votes

I just want to build upon Serpens answer and add the line:

logger.setLevel('DEBUG')

This will allow you to chose what level of message gets logged.

For example in Serpens example,

logger.info('Info message')

Will not get recorded as it defaults to only recording Warnings and above.

More about levels used can be read about here

8
votes

Probably the shortest solution:

def printLog(*args, **kwargs):
    print(*args, **kwargs)
    with open('output.out','a') as file:
        print(*args, **kwargs, file=file)

printLog('hello world')

Writes 'hello world' to sys.stdout and to output.out and works exactly the same way as print().

Note: Please do not specify the file argument for the printLog function. Calls like printLog('test',file='output2.out') are not supported.

3
votes

You should use the logging library, which has this capability built in. You simply add handlers to a logger to determine where to send the output.

2
votes

The easiest solution is to redirect the standard output. In your python program file use the following:

if __name__ == "__main__":
   sys.stdout = open('file.log', 'w')
   #sys.stdout = open('/dev/null', 'w')
   main()

Any std output (e.g. the output of print 'hi there') will be redirected to file.log or if you uncomment the second line, any output will just be suppressed.

0
votes

Create an output file and custom function:

outputFile = open('outputfile.log', 'w')

def printing(text):
    print(text)
    if outputFile:
        outputFile.write(str(text))

Then instead of print(text) in your code, call printing function.

printing("START")
printing(datetime.datetime.now())
printing("COMPLETE")
printing(datetime.datetime.now())
0
votes

Here's a small improvement that to @UltraInstinct's Tee class, modified to be a context manager and also captures any exceptions.

import traceback
import sys

# Context manager that copies stdout and any exceptions to a log file
class Tee(object):
    def __init__(self, filename):
        self.file = open(filename, 'w')
        self.stdout = sys.stdout

    def __enter__(self):
        sys.stdout = self

    def __exit__(self, exc_type, exc_value, tb):
        sys.stdout = self.stdout
        if exc_type is not None:
            self.file.write(traceback.format_exc())
        self.file.close()

    def write(self, data):
        self.file.write(data)
        self.stdout.write(data)

    def flush(self):
        self.file.flush()
        self.stdout.flush()

To use the context manager:

print("Print")
with Tee('test.txt'):
    print("Print+Write")
    raise Exception("Test")
print("Print")