log_capture.py 1.9 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061
  1. """
  2. 并发日志捕获工具
  3. 支持多个并发任务独立记录日志,避免日志混乱
  4. 参考 content_finder 的日志捕获实现
  5. """
  6. import logging
  7. import contextvars
  8. from io import StringIO
  9. from typing import Optional
  10. # 上下文变量:每个任务独立的日志缓冲区
  11. log_buffer_var: contextvars.ContextVar[Optional[StringIO]] = contextvars.ContextVar(
  12. "log_buffer", default=None
  13. )
  14. class ContextBufferHandler(logging.Handler):
  15. """将日志写入上下文变量的缓冲区"""
  16. def emit(self, record: logging.LogRecord):
  17. """将日志记录格式化后写入当前上下文的缓冲区(无缓冲区则忽略)"""
  18. buffer = log_buffer_var.get()
  19. if buffer is not None:
  20. msg = self.format(record)
  21. buffer.write(msg + "\n")
  22. def setup_concurrent_logging():
  23. """设置并发日志系统"""
  24. root_logger = logging.getLogger()
  25. # 添加上下文缓冲处理器
  26. buffer_handler = ContextBufferHandler()
  27. buffer_handler.setFormatter(
  28. logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
  29. )
  30. root_logger.addHandler(buffer_handler)
  31. class LogCapture:
  32. """日志捕获上下文管理器"""
  33. def __init__(self):
  34. """初始化独立的日志缓冲区"""
  35. self.buffer = StringIO()
  36. self.token = None
  37. def __enter__(self):
  38. """进入上下文:将缓冲区绑定到当前上下文变量"""
  39. self.token = log_buffer_var.set(self.buffer)
  40. return self
  41. def __exit__(self, exc_type, exc_val, exc_tb):
  42. """退出上下文:恢复上下文变量到之前的状态"""
  43. log_buffer_var.reset(self.token)
  44. def get_logs(self) -> str:
  45. """获取捕获的日志"""
  46. return self.buffer.getvalue()
  47. # 使用示例
  48. # with LogCapture() as capture:
  49. # logger.info("这条日志会被捕获")
  50. # logs = capture.get_logs()