如何用Python logging捕获第三方非Python库的输出并整合日志
解决第三方C++库print消息纳入Python logging体系的问题
要把第三方库的print输出纳入你的logging流程,同时严格保留消息的执行顺序,我们可以通过重定向sys.stdout到自定义流的方式实现——这个自定义流会把捕获到的内容转发给logging模块,同时不破坏你原本的控制台输出逻辑。
核心思路
第三方库的print调用本质是向sys.stdout写入内容,我们可以替换sys.stdout为一个自定义对象,它在接收到写入请求时会做两件事:
- 先把内容输出到原来的控制台(保持你原本的控制台输出行为)
- 再把内容用指定的日志级别(比如
INFO)写入你的logger,这样file_handler就能捕获到这条消息,同时完美保留执行顺序
完整实现代码
import logging import sys from io import StringIO # 假设无法修改这个第三方函数 def third_party_function(): print("Inside of 'third_party_function'.") def my_helper(): logger.debug("Inside of 'my_helper', before third party call.") third_party_function() logger.warning("Finished with third party call.") class RedirectStream: def __init__(self, original_stream, logger, log_level=logging.INFO): self.original_stream = original_stream self.logger = logger self.log_level = log_level self.buffer = StringIO() def write(self, message): # 先输出到原控制台,保持原有输出行为 self.original_stream.write(message) self.original_stream.flush() # 处理日志记录:过滤空消息和纯换行符,避免冗余日志 self.buffer.write(message) if message.endswith('\n'): log_message = self.buffer.getvalue().strip() if log_message: self.logger.log(self.log_level, log_message) self.buffer = StringIO() def flush(self): self.original_stream.flush() self.buffer.flush() # 初始化logging配置 root_logger = logging.getLogger() root_logger.setLevel(logging.NOTSET) logger = logging.getLogger("mylogger") stream_handler = logging.StreamHandler() stream_handler.setLevel(logging.WARNING) file_handler = logging.FileHandler(filename="progress.out") file_handler.setLevel(logging.NOTSET) logger.addHandler(stream_handler) logger.addHandler(file_handler) # 重定向stdout到自定义流 original_stdout = sys.stdout sys.stdout = RedirectStream(original_stdout, logger, logging.INFO) try: my_helper() finally: # 恢复原来的stdout,避免影响后续代码执行 sys.stdout = original_stdout
效果验证
运行后:
- 控制台输出和你原本的完全一致:
Inside of 'third_party_function'. Finished with third party call. progress.out文件内容会严格按执行顺序包含所有消息:Inside of 'my_helper', before third party call. Inside of 'third_party_function'. Finished with third party call.
自定义调整选项
- 如果希望第三方消息在控制台也按logging的级别过滤,可以修改
log_level参数(比如设为logging.WARNING),这样stream_handler会根据自身级别决定是否输出 - 如果不需要保留第三方消息的控制台输出,可以删除
RedirectStream.write中对original_stream的写入逻辑
内容的提问来源于stack exchange,提问作者M.A.R.
相关产品推荐
相关产品推荐

