如何为重定向到文件的stderr内容添加日期时间前缀?
问题
我已经用logging模块(logger.info、logger.debug等)实现了规范的日志记录,日志会写入文件。但在一些极端场景(比如外部模块输出、未捕获异常等)下,还是有错误输出到stderr。
我用以下代码把stdout和stderr重定向到文件:
import sys sys.stdout, sys.stderr = open("stdout.log", "a+", buffering=1), open("stderr.log", "a+", buffering=1) print("hello") 1/0
这段代码能正常工作,但我想给每条错误内容前加上日期时间前缀。
注:我希望避免使用logging模块,采用更底层的实现方式;同时也不想用下面这个方案:
def exc_handler(ex_cls, ex, tb): with open('mylog.log', 'a') as f: dt = time.strftime('%Y-%m-%d %H:%M:%S') f.write(f"{dt}\n") traceback.print_tb(tb, file=f) f.write(f"{dt}\n") sys.excepthook = exc_handler
因为有些外部模块可能会覆盖sys.excepthook。有没有类似重写sys.stderr.print的底层解决方案?
解决方案
可以自定义一个封装类,替代原生的文件对象,重写write方法实现自动添加日期时间前缀,再用这个类的实例替换sys.stderr和sys.stdout。这种底层重定向方式不受sys.excepthook被覆盖的影响。
示例代码如下:
import sys import time class TimestampedStream: def __init__(self, file_path, mode='a+', buffering=1): self.file = open(file_path, mode, buffering=buffering) self.buffer = "" # 缓存未完成的行内容 def write(self, data): self.buffer += data # 按换行分割,仅处理完整的行 lines = self.buffer.split('\n') # 剩余未换行的内容放回缓存 self.buffer = lines.pop() if lines else "" timestamp = time.strftime('%Y-%m-%d %H:%M:%S') for line in lines: if line: self.file.write(f"[{timestamp}] {line}\n") else: self.file.write("\n") def flush(self): # 处理缓存中剩余的内容 if self.buffer: timestamp = time.strftime('%Y-%m-%d %H:%M:%S') self.file.write(f"[{timestamp}] {self.buffer}\n") self.buffer = "" self.file.flush() def close(self): self.flush() self.file.close() # 替换标准输出和标准错误 sys.stdout = TimestampedStream("stdout.log", "a+", buffering=1) sys.stderr = TimestampedStream("stderr.log", "a+", buffering=1) # 测试代码 print("hello stdout") 1/0 # 触发的异常信息会自动带上时间前缀写入stderr.log
关键说明
- 缓存机制:避免半行内容就添加前缀,确保只有完整的一行输出才会带上时间戳。
- 兼容性:所有输出到
stdout/stderr的内容(包括外部模块输出、未捕获异常栈)都会被处理,无需依赖sys.excepthook。 - 资源处理:实现
flush和close方法,确保程序退出或刷新时,缓存中的剩余内容能被正确写入。
内容的提问来源于stack exchange,提问作者Basj
相关产品推荐
相关产品推荐

