logs_websocket.py 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102
  1. """
  2. Logs WebSocket - 实时推送后端日志到前端
  3. """
  4. import asyncio
  5. import logging
  6. from typing import Set
  7. from fastapi import APIRouter, WebSocket, WebSocketDisconnect
  8. from datetime import datetime
  9. router = APIRouter(prefix="/api/logs", tags=["logs"])
  10. # 存储所有连接的WebSocket客户端
  11. _clients: Set[WebSocket] = set()
  12. class WebSocketLogHandler(logging.Handler):
  13. """自定义日志处理器,将日志推送到WebSocket客户端"""
  14. def emit(self, record: logging.LogRecord):
  15. """发送日志记录到所有连接的客户端"""
  16. try:
  17. log_entry = self.format(record)
  18. # 构造日志消息
  19. message = {
  20. "timestamp": datetime.now().isoformat(),
  21. "level": record.levelname,
  22. "name": record.name,
  23. "message": log_entry,
  24. }
  25. # 异步发送到所有客户端
  26. asyncio.create_task(broadcast_log(message))
  27. except Exception:
  28. self.handleError(record)
  29. async def broadcast_log(message: dict):
  30. """广播日志消息到所有连接的客户端"""
  31. disconnected = set()
  32. for client in _clients:
  33. try:
  34. await client.send_json(message)
  35. except Exception:
  36. disconnected.add(client)
  37. # 移除断开连接的客户端
  38. for client in disconnected:
  39. _clients.discard(client)
  40. @router.websocket("/watch")
  41. async def logs_websocket(websocket: WebSocket):
  42. """
  43. 日志WebSocket端点
  44. 客户端连接后,实时接收后端日志
  45. """
  46. await websocket.accept()
  47. _clients.add(websocket)
  48. try:
  49. # 发送欢迎消息
  50. await websocket.send_json({
  51. "timestamp": datetime.now().isoformat(),
  52. "level": "INFO",
  53. "name": "logs_websocket",
  54. "message": "Connected to logs stream",
  55. })
  56. # 保持连接,等待客户端断开
  57. while True:
  58. # 接收客户端消息(用于保持连接)
  59. await websocket.receive_text()
  60. except WebSocketDisconnect:
  61. pass
  62. finally:
  63. _clients.discard(websocket)
  64. def setup_websocket_logging(level=logging.INFO):
  65. """
  66. 设置WebSocket日志处理器
  67. 将根日志器的日志推送到WebSocket客户端
  68. """
  69. handler = WebSocketLogHandler()
  70. handler.setLevel(level)
  71. # 设置日志格式
  72. formatter = logging.Formatter(
  73. "%(asctime)s [%(levelname)s] %(name)s: %(message)s",
  74. datefmt="%Y-%m-%d %H:%M:%S"
  75. )
  76. handler.setFormatter(formatter)
  77. # 添加到根日志器
  78. root_logger = logging.getLogger()
  79. root_logger.addHandler(handler)
  80. return handler