Search code examples
pythondecoratoroutput-redirect

Logging isn't working properly


So I am trying to write a decorator which will log the standard output, while print it as well. The whole point is to fork the output so that it will show in the console, as well as get logged. So this is what I have so far:

from time import time
import tempfile
import sys
import datetime 

def printUsingTempFile(f):

    def wrapper(*a, **ka):
        with open('sysLog.txt', 'a')  as logFile:
            with tempfile.NamedTemporaryFile() as f:
                sys.stdout = f
                sys.stderr = f 

                retVal = f(*a, **ka)

                f.flush(); f.seek(0); logFile.write('\n'.join(f.readlines()))
                f.flush(); f.seek(0); sys.__stdout__.write('\n'.join(f.readlines()))

                sys.stdout = sys.__stdout__
                sys.stderr = sys.__stderr__

        return retVal
    return wrapper 

if __name__ == '__main__':

    @printUsingTempFile
    def someFunc(abc='Hello!!!'):
        '''
        This is the docstring of the someFunc function.
        '''
        print abc
        return 1

    someFunc('Print something here')

I run it like so:

In [18]: !python utility.py

because if I run is by using the run function the stupid iPython window closes.

I am getting a new file called 'sysLog.txt' in the directory, but that is empty. And there is no output anywhere to be seen either. There is no error or anything. Just ... nothing! Getting a little frustrated here with this. Not sure how to debug this code :(

Edit:

I realized that I wasn't seeking twice so I updated the code. I tried the following code:

def printUsingTempFile1():

    with open('sysLog.txt', 'a')  as logFile:
        with tempfile.NamedTemporaryFile() as f:
            sys.stdout = f
            sys.stderr = f 

            print 'This is something ...'

            f.flush(); f.seek(0); logFile.write('\n'.join(f.readlines()))
            f.flush(); f.seek(0); sys.__stdout__.write('\n'.join(f.readlines()))

        sys.stdout = sys.__stdout__
        sys.stderr = sys.__stderr__

And this function seems to be working ok.


Solution

  • Why are you using f as the name for the passed in function to the decorator as well as the name of the temporary file? I'm assuming that the call to f(*a, **ka) errors out and since you have redirected the error stream to the temp file, it gets closed without writing out anything and hence nothing on the console.

    A tip for debugging these sort of situations is to start stripping pieces of code which doesn't work one by one and bringing it down to the bare minimum which works. And then again work your way up till it starts failing. :)

    EDIT

    I would also like to recommend running your code under a lint tool to catch issues like these. For e.g. the code posted below:

    ''' This is a test module '''
    import tempfile
    import sys
    
    
    def print_tmp_file(arg):
        ''' Print out the things using temp file '''
        def wrapper(*a, **ka):
        ''' Wrapper function '''
            with open('sysLog.txt', 'a')  as log_file:
                with tempfile.NamedTemporaryFile() as arg:
                    sys.stdout = arg
                    sys.stderr = arg
                    ret_val = arg(*a, **ka)
                    arg.flush()
                    arg.seek(0)
                    log_file.write('\n'.join(arg.readlines()))
                    arg.flush()
                    arg.seek(0)
                    sys.__stdout__.write('\n'.join(arg.readlines()))
                    sys.stdout = sys.__stdout__
                    sys.stderr = sys.__stderr__
                    return ret_val
    
        return wrapper
    
    
    if __name__ == '__main__':
        @print_tmp_file
        def some_func(abc='Hello!!!'):
            ''' This is the docstring of the someFunc function. '''
            print abc
            return 1
    
        some_func('Print something here')
    

    Running pylint on the above code gives the following warning:

    W: 6,19: Unused argument 'arg' (unused-argument)

    which is a clear indication that something has gone wrong.