ForcePilot/backend/package/yuxi/channel/extensions/nostr/metrics.py
Kris 16455cb303 feat(channel): 添加 Nostr 渠道扩展
新增 Nostr 渠道扩展,支持在 Yuxi 平台中集成 Nostr 去中心化社交协议。

包含以下功能模块:
- bus: 事件总线与中继通信
- account: 账户管理
- key_utils: 密钥工具
- gateway: SSE/WebSocket 网关接入
- outbound: 外发消息管理
- gift_wrap: Gift Wrap 加密
- nip44: NIP-44 加密协议
- config_schema: 配置模式
- defaults: 默认配置
- profile_core: 用户资料核心
- profile_publisher: 资料发布
- event_utils: 事件工具
- deletion: 事件删除
- reactions: 表情反应
- metrics: 指标监控
- seen_tracker: 已读追踪
- session_route: 会话路由
- state_store: 状态存储
2026-05-21 11:31:51 +08:00

104 lines
3.8 KiB
Python

import time
from collections import defaultdict
from collections.abc import Callable
from dataclasses import dataclass, field
@dataclass
class RelayMetrics:
connect: int = 0
disconnect: int = 0
reconnect: int = 0
error: int = 0
event: int = 0
eose: int = 0
closed: int = 0
notice: int = 0
ok: int = 0
auth: int = 0
circuit_breaker_open: int = 0
circuit_breaker_close: int = 0
circuit_breaker_half_open: int = 0
@dataclass
class MetricsSnapshot:
events_received: int = 0
events_processed: int = 0
events_duplicate: int = 0
events_self_message: int = 0
events_rejected: dict[str, int] = field(default_factory=dict)
relays: dict[str, RelayMetrics] = field(default_factory=dict)
rate_limiting: dict[str, int] = field(
default_factory=lambda: {"per_sender": 0, "global": 0}
)
decrypt: dict[str, int] = field(
default_factory=lambda: {"success": 0, "failure": 0}
)
memory: dict[str, int] = field(
default_factory=lambda: {"seen_tracker_size": 0, "rate_limiter_entries": 0}
)
snapshot_at: float = 0.0
class MetricsCollector:
def __init__(self, on_metric: Callable | None = None):
self._counters: dict[str, int] = defaultdict(int)
self._relay_stats: dict[str, dict] = defaultdict(lambda: defaultdict(int))
self._on_metric = on_metric
def emit(self, name: str, relay: str | None = None, value: int = 1):
self._counters[name] += value
if relay:
self._relay_stats[relay][name] += value
if self._on_metric:
self._on_metric(name, relay, value)
def snapshot(
self, seen_tracker_size: int = 0, rate_limiter_entries: int = 0
) -> MetricsSnapshot:
rejected = {}
for k, v in self._counters.items():
if k.startswith("event.rejected."):
rejected[k.replace("event.rejected.", "")] = v
relay_snapshots = {}
for relay, stats in self._relay_stats.items():
relay_snapshots[relay] = RelayMetrics(
connect=stats.get("relay.connect", 0),
disconnect=stats.get("relay.disconnect", 0),
reconnect=stats.get("relay.reconnect", 0),
error=stats.get("relay.error", 0),
event=stats.get("relay.message.event", 0),
eose=stats.get("relay.message.eose", 0),
closed=stats.get("relay.message.closed", 0),
notice=stats.get("relay.message.notice", 0),
ok=stats.get("relay.message.ok", 0),
auth=stats.get("relay.message.auth", 0),
circuit_breaker_open=stats.get("relay.circuit_breaker.open", 0),
circuit_breaker_close=stats.get("relay.circuit_breaker.close", 0),
circuit_breaker_half_open=stats.get("relay.circuit_breaker.half_open", 0),
)
return MetricsSnapshot(
events_received=self._counters.get("event.received", 0),
events_processed=self._counters.get("event.processed", 0),
events_duplicate=self._counters.get("event.duplicate", 0),
events_self_message=self._counters.get("event.rejected.self_message", 0),
events_rejected=rejected,
relays=relay_snapshots,
rate_limiting={
"per_sender": self._counters.get("rate_limit.per_sender", 0),
"global": self._counters.get("rate_limit.global", 0),
},
decrypt={
"success": self._counters.get("decrypt.success", 0),
"failure": self._counters.get("decrypt.failure", 0),
},
memory={
"seen_tracker_size": seen_tracker_size,
"rate_limiter_entries": rate_limiter_entries,
},
snapshot_at=time.time(),
)