January 10th, 2008, 12:45 AM
How to redirect stdout and stderr to a file?
I'd like to redirect stderr to a file so if a program crashes there's a text file record of it. I gather this involves subclassing stdout but I don't know how to do that.
Anyone feel like steering me in the right direction?
Thanks for any help.
January 10th, 2008, 05:06 PM
Just as a matter of clarification: did you need to redirect stderr (send the output to file instead of to the terminal) or log it (duplicate the output into a file)? Also, does this need to redirect/log all error messages (including those from the interpreter) or only those from your own program?
If all you needed was to have the program's error messages redirected to a file, and don't expect to need to set it back to the console, then it is straightforward: sys.stderr is a variable, and can in fact be reassigned to a different output stream. See here for details (section 10.10). Alternately, you can assign your own variable to stderr, using it instead of standard error for posting error messages, and then reassign it to a file as needed.
For logging, you might want to look into the Logger class, which gives better structuring and control over error messages than writing directly to sys.stderr in any case. This blog page gives an example of the use of the logging tools.
Last edited by Schol-R-LEA; January 10th, 2008 at 05:17 PM.
January 10th, 2008, 05:13 PM
You don't need to subclass anything. Here are the steps you need:
1) import sys
2) open the file you want to write to
3) assign the file to sys.stderr
If you want to restore stderr back to the terminal then you will need to save the original stderr object before you assign to it.
f = open("myfile.log", "w")
original_stderr = sys.stderr
sys.stderr = f
... do stuff
sys.stderr = original_stderr
Alternatively look at the logging module for a better way of writing out debug and diagnostic information.
January 11th, 2008, 06:52 AM
Thanks, this works well for redirecting to a file.
I was wondering if it might be possible to redirect it to a function? That way I could, for example, send it to a text control in a GUI program.
For example, suppose I had a queue that I'd like to append the error to. Something like:
msgQueue = Queue.Queue() # defined elsewhere, used for other messages too
sys.stderr = msgQueue.put(error)
Of course, in the put statement above I'm not referencing the error properly, it's just to get the point across.
Any tips on how I might accomplish this?
Last edited by wrybread; January 11th, 2008 at 07:12 AM.
January 12th, 2008, 06:10 AM
You can replace stderr with any object that has the same methods as a file. The only one you really need to implement is write - the others you can replace with do-nothing stubs, or leave them off altogether if they never get called. Something like this (warning, untested code ahead):
def __init__(self, queue):
self.queue = queue
def write(self, string):
sys.stderr = QueueWriter(msgQueue)
January 13th, 2008, 12:49 AM
Thanks, works perfectly.
I don't exactly understand why though. Is it possible to explain why the write() function is triggered? And how "string" got passed to it?
And are close() and flush() ever executed?
And for anyone else coming down this path, here's the code I'm using to redirect stderr. This prints the text of any unhandled exceptions to the console as usual, saves to a text file, and pops open a wx dialog window.
def write(self, string):
# write error to console
# write error to file
fname = "crash report (" + time.strftime("%Y-%m-%d %I-%M%p") + ").txt"
handler = open(fname, "w")
# write error to popup dialog
dlg = wx.MessageDialog(None, string, "Crash Report", wx.ICON_HAND)
#os._exit(1) # close the program
# not sure if these are necessary:
original_stderr = sys.stderr # stored in case want to revert
sys.stderr = ErrorHandler()
January 18th, 2008, 11:17 PM
It works because of 'duck typing' in Python. All Python cares about about the sys.stdout and sys.stderr objects is that they have the neccesary methods.
(Duck typing: if it quakes like a duck, and looks like a duck, its a duck.)