ForcePilot/backend/package/yuxi/channels/adapters/nextcloudtalk/metrics.py
Kris b018ad25da feat(backend): add Nextcloud Talk channel adapter implementation
实现了完整的Nextcloud Talk聊天适配器,包含以下核心功能:
1. 基础客户端通信与认证
2. 会话路由与聊天类型解析
3. 消息分块与格式转换
4. 用户配对与权限校验
5. 轮询式消息监听
6. 配置验证与诊断工具
7. 安全策略与去重缓存
8. 指标统计与状态快照

新增配套的配对用户缓存文件与模块导出入口。
2026-05-12 00:47:06 +08:00

79 lines
3.2 KiB
Python

from __future__ import annotations
import time
from collections import deque
from typing import Any
class NextcloudTalkMetrics:
def __init__(self, max_latency_samples: int = 100):
self._max_latency_samples = max_latency_samples
self._send_latencies: deque[float] = deque(maxlen=max_latency_samples)
self._receive_latencies: deque[float] = deque(maxlen=max_latency_samples)
self._send_count: int = 0
self._receive_count: int = 0
self._send_error_count: int = 0
self._receive_error_count: int = 0
self._circuit_breaker_trips: int = 0
self._started_at: float = time.time()
def record_send_success(self, latency_s: float) -> None:
self._send_latencies.append(latency_s)
self._send_count += 1
def record_send_error(self) -> None:
self._send_error_count += 1
def record_receive_success(self, latency_s: float) -> None:
self._receive_latencies.append(latency_s)
self._receive_count += 1
def record_receive_error(self) -> None:
self._receive_error_count += 1
def record_circuit_breaker_trip(self) -> None:
self._circuit_breaker_trips += 1
def snapshot(self) -> dict[str, Any]:
uptime_s = time.time() - self._started_at
send_latencies = list(self._send_latencies)
recv_latencies = list(self._receive_latencies)
avg_send = sum(send_latencies) / len(send_latencies) if send_latencies else None
avg_recv = sum(recv_latencies) / len(recv_latencies) if recv_latencies else None
return {
"uptime_seconds": round(uptime_s, 1),
"send": {
"count": self._send_count,
"error_count": self._send_error_count,
"avg_latency_ms": round(avg_send * 1000, 1) if avg_send is not None else None,
"p50_latency_ms": round(_percentile(send_latencies, 50) * 1000, 1) if send_latencies else None,
"p95_latency_ms": round(_percentile(send_latencies, 95) * 1000, 1) if send_latencies else None,
"p99_latency_ms": round(_percentile(send_latencies, 99) * 1000, 1) if send_latencies else None,
},
"receive": {
"count": self._receive_count,
"error_count": self._receive_error_count,
"avg_latency_ms": round(avg_recv * 1000, 1) if avg_recv is not None else None,
"p50_latency_ms": round(_percentile(recv_latencies, 50) * 1000, 1) if recv_latencies else None,
"p95_latency_ms": round(_percentile(recv_latencies, 95) * 1000, 1) if recv_latencies else None,
"p99_latency_ms": round(_percentile(recv_latencies, 99) * 1000, 1) if recv_latencies else None,
},
"circuit_breaker": {
"trip_count": self._circuit_breaker_trips,
},
}
def _percentile(data: list[float], percentile: float) -> float:
if not data:
return 0.0
sorted_data = sorted(data)
k = (len(sorted_data) - 1) * percentile / 100.0
f = int(k)
c = k - f
if f + 1 < len(sorted_data):
return sorted_data[f] + c * (sorted_data[f + 1] - sorted_data[f])
return sorted_data[f]