Search code examples
pythonpython-2.7loggingpython-2.6stderr

Python: capturing both of sys.stdout and sys.stderr as a log file


Roughly speaking, I want to port this to pure Python:

#!/bin/bash

{
    python test.py
} &> /tmp/test.log

This didn't work for some unknown reasons:

import os.path, sys
import tempfile

with open(os.path.join(tempfile.gettempdir(), "test.log"), "a") as fp:
    sys.stdout = sys.stderr = fp
    raise Exception("I'm dying")

The resulting test.log was empty (and I didn't see anything on my console,) when I tested it with Python 2.6.6, Python 2.7.8 and Python 3.4.2 on CentOS x86_64.

But Ideally I'd like a solution for Python 2.6.

(For now, it's tolerable to clutter the log file with intermixed output from stdout and stderr or multithreading, as long as any data won't simply disappear into a blackhole.)

Show me a concise and portable solution which is confirmed to work with an exception stack trace on sys.stderr. (Preferably something other than os.dup2)


Solution

  • This works for me:

    #!/usr/bin/env python
    from __future__ import print_function
    import os, os.path, sys, tempfile
    
    old_out = os.dup(sys.stdout.fileno())
    old_err = os.dup(sys.stderr.fileno())
    with open(os.path.join(tempfile.gettempdir(), "test.log"), "a") as fp:
        fd = fp.fileno()
        os.dup2(fd, sys.stdout.fileno())
        os.dup2(fd, sys.stderr.fileno())
        print("Testing")
        print('testing errs', file=sys.stderr)
        raise Exception("I'm dying")
    

    The future is just for cleaner handling of Python2 or Python3 with the same example. (I've also changed the raise statement to instantiate an exception, strings as exceptions have been deprecated for a long time and they're not properly supported under Python3).

    The old_* values are just if we wanted to restore our original stdout and/or stderr after using our redirected file.