102 lines
3.1 KiB
Python
102 lines
3.1 KiB
Python
"""任务级日志缓冲区,收集单个任务的所有日志,任务完成后一次性输出。
|
||
|
||
解决多任务并行执行时日志行交叉混乱的问题:每个任务维护独立的缓冲区,
|
||
任务完成后将完整日志按时间顺序一次性输出到父 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)
|