When you run a Python script from the terminal, most normal output goes to stdout, while warnings and errors usually go to stderr. Keeping those streams separate is useful, but sometimes you want to temporarily capture both of them: maybe you are testing a noisy function, saving diagnostic output to a file, or wrapping a command-line helper inside a larger automation script.
Python makes this easy with contextlib.redirect_stdout and contextlib.redirect_stderr. These context managers only affect the indented block, so the rest of your program returns to normal terminal behavior as soon as the block exits. That makes the pattern safer than permanently reassigning sys.stdout or sys.stderr.
from contextlib import redirect_stdout, redirect_stderr
from pathlib import Path
log_path = Path("terminal-output.log")
def noisy_task():
print("Starting work...")
print("Something noteworthy happened", file=__import__("sys").stderr)
print("Finished work.")
with log_path.open("w", encoding="utf-8") as log_file:
with redirect_stdout(log_file), redirect_stderr(log_file):
noisy_task()
print(f"Captured output in {log_path}")
In this example, anything printed by noisy_task() is written into terminal-output.log. The final print() call runs outside the redirect block, so it still appears in your terminal. This is handy when you want a script to keep a clean screen while preserving a detailed record for later inspection.
This pattern is also useful when you are debugging scheduled jobs or small maintenance scripts. Instead of sprinkling custom logging calls everywhere, you can wrap one focused section, capture exactly what it prints, and then decide later whether that output should become structured logging, test assertions, or a temporary troubleshooting artifact.
If you need to capture output in memory instead of writing it to disk, use io.StringIO as the target. That approach works well in tests because you can assert against the captured text without creating temporary files. For longer-running scripts, a real log file is usually better because it survives crashes and can be reviewed independently.
One important detail: redirection affects Python-level writes to sys.stdout and sys.stderr. It will not automatically capture output from every external process unless that process is launched with its own stdout and stderr pipes. For subprocesses, use the subprocess module’s capture options.
For more background on this pattern and an older custom context-manager approach, see Marc Abramowitz’s article on a Python context manager for redirected stdout and stderr.
If your team wants practical engineering help turning small automation patterns like this into reliable production workflows, connect with ViWeb Technology for web development, hosting, and technical operations support.