直接赋值sys.stdout重定向虽简单,但存在缓冲丢失、无法恢复、多线程冲突和资源泄漏等风险;推荐使用contextlib.redirect_stdout(作用域安全)或logging模块(生产日志首选)。

用 sys.stdout 直接赋值重定向最简单但有风险
Python 允许直接给 sys.stdout 赋一个打开的文件对象,比如 sys.stdout = open("app.log", "a")。这确实能让所有 print() 输出进文件,但问题不少:
— 不自动刷新,日志可能延迟甚至丢失(尤其程序异常退出时)
— 原始 sys.stdout 被覆盖后,无法轻易恢复,调试时容易抓瞎
— 多线程下不安全,多个线程同时写同一个文件句柄会错乱
— 文件没被正确关闭,可能触发资源泄漏
推荐用 contextlib.redirect_stdout 控制作用域
这是标准库提供的安全方式,只在指定代码块内生效,退出自动还原,且支持刷新和上下文管理:
from contextlib import redirect_stdout
import sys
<p>with open("app.log", "a") as f:
with redirect_stdout(f):
print("这条会进日志")
print("这条也是")</p><h1>退出后,print() 恢复输出到终端</h1><p></p>
注意:
— 必须确保文件以文本模式(如 "a" 或 "w")打开,不能是二进制模式
— 如果想实时看到日志,可在 print() 中加 flush=True,或在 open() 里加 buffering=1
— 不适用于长期运行服务——它是一次性上下文,不能“一直开着”
长期服务该用 logging 模块而非重定向 stdout
真正要写生产日志,别碰 sys.stdout 重定向。用 logging 是唯一靠谱选择:
import logging
<p>logging.basicConfig(
level=logging.INFO,
format="%(asctime)s - %(levelname)s - %(message)s",
handlers=[logging.FileHandler("app.log", encoding="utf-8")]
)</p><p>logging.info("启动成功")
print("这行仍输出到终端,不影响日志") # print 和 logging 完全解耦
</p>
关键点:
— print() 和日志彻底分离:你想打哪儿打哪儿,互不干扰
— 支持日志轮转(RotatingFileHandler)、多处理器安全、等级过滤
— basicConfig 只生效一次,后续调用无效;若需动态调整,直接操作 root logger 或自定义 logger
— 切忌在多进程场景下共用同一文件 handler,得用 QueueHandler + QueueListener 或改用文件锁
临时调试时用 tee 类似效果(Linux/macOS)
如果只是跑脚本时顺便存一份日志,命令行比改代码更轻量:
python myscript.py | tee app.log
这样终端能看到输出,同时追加写入文件。但要注意:
— Python 的 print() 在管道中默认行缓冲,一般没问题;但用了 end="" 或输出非换行内容时,可能卡住,可加 -u 参数强制无缓冲:python -u myscript.py | tee app.log
— Windows 默认没有 tee,可用 python -c "import sys; [print(line, end='') for line in sys.stdin]" 模拟,但不如原生命令稳定
— 这种方式绕过了 Python 的 I/O 层,无法捕获 sys.stderr(报错信息),如需一并记录,得用 2>&1 | tee app.log
真正难的不是“怎么写进文件”,而是决定哪些输出该进日志、是否需要结构化、要不要跨进程一致、以及出问题时能不能快速定位——这些决定了你该用 logging 的哪一层 API,而不是纠结 sys.stdout 指向谁。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











