Files
myaps_api/apps/common/monitor/log_stream_service.py
T
chaoge bf42299ead 重构: 迁移至统一日志系统
- 新增 globalobjects/logger/ 模块化日志系统
- 支持异步写入、多目标输出、敏感信息脱敏
- 完全向后兼容原有logger API
- 备份旧版本为 logger_v1_backup.py 和 logger_v2_backup.py
- 更新 .env.example 和 AGENTS.md 文档
2026-05-22 00:23:30 +08:00

214 lines
6.9 KiB
Python

"""
日志流服务 - 完全独立于现有日志系统
通过添加额外的日志处理器来收集日志,不影响现有日志输出
"""
import asyncio
import logging
from collections import deque
from typing import Set, List, Dict, Optional
# 延迟导入避免循环依赖
def _get_logger():
from globalobjects import logger as log_config
return log_config.get_logger(__name__)
def _get_log_stream_manager():
"""获取统一的日志流管理器(来自logger系统)"""
from globalobjects.logger.handlers import _log_stream_manager
return _log_stream_manager
class LogStreamManager:
"""全局日志流管理器 - 单例模式,确保所有模块使用同一个实例"""
_instance = None
_lock = asyncio.Lock()
def __new__(cls):
if cls._instance is None:
cls._instance = super().__new__(cls)
cls._instance._handlers: List[logging.Handler] = []
cls._instance._initialized = True
return cls._instance
def add_handler(self, handler: logging.Handler):
"""添加日志流处理器"""
if handler not in self._handlers:
self._handlers.append(handler)
def remove_handler(self, handler: logging.Handler):
"""移除日志流处理器"""
if handler in self._handlers:
self._handlers.remove(handler)
def emit_to_handlers(self, record: logging.LogRecord):
"""将日志发送到所有注册的处理器"""
for handler in self._handlers:
try:
handler.emit(record)
except Exception:
pass
def get_handlers(self) -> List[logging.Handler]:
"""获取所有注册的处理器"""
return self._handlers.copy()
# 全局日志流管理器实例
_log_stream_manager = LogStreamManager()
class LogStreamService:
"""独立的日志流服务"""
def __init__(self, max_queue_size: int = 1000):
self._log_queue = deque(maxlen=max_queue_size)
self._history_logs = deque(maxlen=max_queue_size) # 独立的历史日志存储
self._active_connections: Set[asyncio.Queue] = set()
self._lock = asyncio.Lock()
self._is_running = False
self._broadcast_task = None
self._handler = None
self._registered_loggers = set() # 记录已注册的logger
async def start(self):
"""启动日志流服务"""
import sys
# print("[日志流] start() 被调用", flush=True)
self._is_running = True
self._broadcast_task = asyncio.create_task(self._broadcast_logs())
# 添加日志处理器(不影响现有日志)
self._add_log_handler()
# 使用 print 避免递归调用 logging
# print("[日志流] 服务已启动", flush=True)
sys.stdout.flush()
async def stop(self):
"""停止日志流服务"""
self._is_running = False
if self._broadcast_task:
self._broadcast_task.cancel()
# 移除日志处理器
self._remove_log_handler()
async with self._lock:
for queue in self._active_connections:
await queue.put(None)
_get_logger().info("[日志流] 服务已停止")
def _add_log_handler(self):
"""添加自定义日志处理器(不影响现有处理器)"""
if self._handler is None:
self._handler = _LogStreamHandler(self)
self._handler.setLevel(logging.DEBUG)
manager = _get_log_stream_manager()
manager.add_handler(self._handler)
def _remove_log_handler(self):
"""移除日志处理器"""
if self._handler:
manager = _get_log_stream_manager()
manager.remove_handler(self._handler)
self._registered_loggers.clear()
self._handler = None
def enqueue_log(self, record: logging.LogRecord):
"""将日志加入队列"""
try:
log_data = {
"timestamp": record.created,
"level": record.levelname,
"module": record.module,
"function": record.funcName,
"line": record.lineno,
"message": record.getMessage(),
"logger_name": record.name
}
self._log_queue.append(log_data)
self._history_logs.append(log_data)
except Exception:
pass
def enqueue_log_dict(self, log_data: dict):
"""直接将日志字典加入队列"""
self._log_queue.append(log_data)
async def subscribe(self) -> asyncio.Queue:
"""订阅日志流"""
queue = asyncio.Queue(maxsize=100)
async with self._lock:
self._active_connections.add(queue)
# 发送最近的历史日志(批量非阻塞方式)
recent_logs = self.get_recent_logs(50)
for log in recent_logs:
try:
queue.put_nowait(log)
except asyncio.QueueFull:
# 队列满时跳过旧日志,优先保留最新的
break
return queue
async def unsubscribe(self, queue: asyncio.Queue):
"""取消订阅"""
async with self._lock:
self._active_connections.discard(queue)
def get_recent_logs(self, count: int = 50) -> List[dict]:
"""获取最近的日志"""
logs = list(self._history_logs)[-count:]
return [log for log in logs if log is not None]
async def _broadcast_logs(self):
"""广播日志到所有订阅者"""
while self._is_running:
if self._log_queue:
log_data = self._log_queue.popleft()
if log_data is None:
continue
async with self._lock:
queues = list(self._active_connections)
for queue in queues:
try:
queue.put_nowait(log_data)
except asyncio.QueueFull:
pass
await asyncio.sleep(0.01)
class _LogStreamHandler(logging.Handler):
"""
内部日志处理器 - 只负责收集日志到日志流服务
不输出到任何地方,不影响现有日志
"""
def __init__(self, service: LogStreamService):
super().__init__(logging.DEBUG) # 捕获所有级别
self._service = service
def emit(self, record: logging.LogRecord):
"""处理日志记录 - 只是收集,不输出"""
try:
self._service.enqueue_log(record)
except Exception:
pass
# 创建全局实例
log_stream_service = LogStreamService()
# 启动/停止函数
async def start_log_stream():
"""启动日志流服务"""
# print("[日志流] start_log_stream 函数被调用")
await log_stream_service.start()
async def stop_log_stream():
"""停止日志流服务"""
# print("[日志流] stop_log_stream 函数被调用")
await log_stream_service.stop()