"""任务级日志缓冲区,收集单个任务的所有日志,任务完成后一次性输出。 解决多任务并行执行时日志行交叉混乱的问题:每个任务维护独立的缓冲区, 任务完成后将完整日志按时间顺序一次性输出到父 logger,添加 [task_code] 前缀。 """ from __future__ import annotations import logging import threading from dataclasses import dataclass, field from datetime import datetime @dataclass class LogEntry: """日志条目。""" timestamp: datetime level: int task_code: str message: str class TaskLogBuffer: """任务级日志缓冲区,收集单个任务的所有日志,任务完成后一次性输出。 所有写入操作线程安全(内部使用 threading.Lock)。 """ def __init__(self, task_code: str, parent_logger: logging.Logger) -> None: """初始化日志缓冲区。 Args: task_code: 任务代码,用于日志前缀标识。 parent_logger: 父 logger,flush() 时日志输出的目标。 """ self.task_code = task_code self._parent = parent_logger self._buffer: list[LogEntry] = [] self._lock = threading.Lock() def log(self, level: int, message: str, *args: object) -> None: """线程安全地缓冲一条日志。 Args: level: 日志级别(如 logging.INFO)。 message: 日志消息,支持 % 格式化。 *args: 格式化参数。 """ formatted = message % args if args else message entry = LogEntry( timestamp=datetime.now(), level=level, task_code=self.task_code, message=formatted, ) with self._lock: self._buffer.append(entry) # ---- 便捷方法 ---- def debug(self, message: str, *args: object) -> None: self.log(logging.DEBUG, message, *args) def info(self, message: str, *args: object) -> None: self.log(logging.INFO, message, *args) def warning(self, message: str, *args: object) -> None: self.log(logging.WARNING, message, *args) def error(self, message: str, *args: object) -> None: self.log(logging.ERROR, message, *args) # ---- 输出 ---- def flush(self) -> list[LogEntry]: """将缓冲区内容按时间顺序一次性输出到父 logger,并清空缓冲区。 输出时每条日志添加 [task_code] 前缀,保证日志归属可识别。 Returns: 按时间戳升序排列的日志条目列表(副本)。 """ with self._lock: entries = sorted(self._buffer, key=lambda e: e.timestamp) for entry in entries: self._parent.log( entry.level, "[%s] %s", entry.task_code, entry.message, ) self._buffer.clear() return list(entries) @property def entries(self) -> list[LogEntry]: """返回当前缓冲区条目的副本(用于测试/检查)。""" with self._lock: return list(self._buffer)