Edit: Since it appears that there's either no solution, or I'm doing something so non-standard that nobody knows - I'll revise my question to also ask: What is the best way to accomplish logging when a python app is making a lot of system calls?
My app has two modes. In interactive mode, I want all output to go to the screen as well as to a log file, including output from any system calls. In daemon mode, all output goes to the log. Daemon mode works great using os.dup2()
. I can't find a way to "tee" all output to a log in interactive mode, without modifying each and every system call.
In other words, I want the functionality of the command line 'tee' for any output generated by a python app, including system call output.
To clarify:
To redirect all output I do something like this, and it works great:
# open our log file
so = se = open("%s.log" % self.name, 'w', 0)
# re-open stdout without buffering
sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)
# redirect stdout and stderr to the log file opened above
os.dup2(so.fileno(), sys.stdout.fileno())
os.dup2(se.fileno(), sys.stderr.fileno())
The nice thing about this is that it requires no special print calls from the rest of the code. The code also runs some shell commands, so it's nice not having to deal with each of their output individually as well.
Simply, I want to do the same, except duplicating instead of redirecting.
At first thought, I thought that simply reversing the dup2
's should work. Why doesn't it? Here's my test:
import os, sys
### my broken solution:
so = se = open("a.log", 'w', 0)
sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)
os.dup2(sys.stdout.fileno(), so.fileno())
os.dup2(sys.stderr.fileno(), se.fileno())
###
print("foo bar")
os.spawnve("P_WAIT", "/bin/ls", ["/bin/ls"], {})
os.execve("/bin/ls", ["/bin/ls"], os.environ)
The file "a.log" should be identical to what was displayed on the screen.
(Ah, just re-read your question and see that this doesn't quite apply.)
Here is a sample program that makes uses the python logging module. This logging module has been in all versions since 2.3. In this sample the logging is configurable by command line options.
In quite mode it will only log to a file, in normal mode it will log to both a file and the console.
None of the answers above really seems to answer the problem posed. I know this is an old thread, but I think this problem is a lot simpler than everyone is making it:
Now this will repeat everything to the normal sys.stderr handler and your file. Create another class
tee_out
forsys.stdout
.I know this question has been answered repeatedly, but for this I've taken the main answer from John T's answer and modified it so it contains the suggested flush and followed its linked revised version. I've also added the enter and exit as mentioned in cladmi's answer for use with the with statement. In addition, the documentation mentions to flush files using
os.fsync()
so I've added that as well. I don't know if you really need that but its there.You can then use it
or
another solution using logging module:
I'm writing a script to run cmd-line scripts. ( Because in some cases, there just is no viable substitute for a Linux command -- such as the case of rsync. )
What I really wanted was to use the default python logging mechanism in every case where it was possible to do so, but to still capture any error when something went wrong that was unanticipated.
This code seems to do the trick. It may not be particularly elegant or efficient ( although it doesn't use string+=string, so at least it doesn't have that particular potential bottle- neck ). I'm posting it in case it gives someone else any useful ideas.
Obviously, if you're not as subject to whimsy as I am, replace LOG_IDENTIFIER with another string that you're not like to ever see someone write to a log.
Since you're comfortable spawning external processes from your code, you could use
tee
itself. I don't know of any Unix system calls that do exactly whattee
does.You could also emulate
tee
using the multiprocessing package (or use processing if you're using Python 2.5 or earlier).