mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-27 12:56:01 -06:00
refactor(nostr): centralize metrics state (#105408)
This commit is contained in:
+70
-119
@@ -150,51 +150,60 @@ export interface NostrMetrics {
|
||||
reset: () => void;
|
||||
}
|
||||
|
||||
type MetricsState = Omit<MetricsSnapshot, "relays" | "snapshotAt"> & {
|
||||
relays: Map<string, RelayMetrics>;
|
||||
};
|
||||
|
||||
function createZeroMetricsState(): MetricsState {
|
||||
return {
|
||||
eventsReceived: 0,
|
||||
eventsProcessed: 0,
|
||||
eventsDuplicate: 0,
|
||||
eventsRejected: {
|
||||
invalidShape: 0,
|
||||
wrongKind: 0,
|
||||
stale: 0,
|
||||
future: 0,
|
||||
rateLimited: 0,
|
||||
invalidSignature: 0,
|
||||
oversizedCiphertext: 0,
|
||||
oversizedPlaintext: 0,
|
||||
decryptFailed: 0,
|
||||
selfMessage: 0,
|
||||
},
|
||||
relays: new Map(),
|
||||
rateLimiting: { perSenderHits: 0, globalHits: 0 },
|
||||
decrypt: { success: 0, failure: 0 },
|
||||
memory: { seenTrackerSize: 0, rateLimiterEntries: 0 },
|
||||
};
|
||||
}
|
||||
|
||||
function createMetricsSnapshot(state: MetricsState, snapshotAt?: number): MetricsSnapshot {
|
||||
const relays: MetricsSnapshot["relays"] = {};
|
||||
for (const [url, stats] of state.relays) {
|
||||
relays[url] = { ...stats, messagesReceived: { ...stats.messagesReceived } };
|
||||
}
|
||||
|
||||
return {
|
||||
...state,
|
||||
eventsRejected: { ...state.eventsRejected },
|
||||
relays,
|
||||
rateLimiting: { ...state.rateLimiting },
|
||||
decrypt: { ...state.decrypt },
|
||||
memory: { ...state.memory },
|
||||
snapshotAt: snapshotAt ?? Date.now(),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a metrics collector instance.
|
||||
* Optionally pass an onMetric callback to receive real-time metric events.
|
||||
*/
|
||||
export function createMetrics(onMetric?: OnMetricCallback): NostrMetrics {
|
||||
// Counters
|
||||
let eventsReceived = 0;
|
||||
let eventsProcessed = 0;
|
||||
let eventsDuplicate = 0;
|
||||
const eventsRejected = {
|
||||
invalidShape: 0,
|
||||
wrongKind: 0,
|
||||
stale: 0,
|
||||
future: 0,
|
||||
rateLimited: 0,
|
||||
invalidSignature: 0,
|
||||
oversizedCiphertext: 0,
|
||||
oversizedPlaintext: 0,
|
||||
decryptFailed: 0,
|
||||
selfMessage: 0,
|
||||
};
|
||||
|
||||
// Per-relay stats
|
||||
const relays = new Map<string, RelayMetrics>();
|
||||
|
||||
// Rate limiting stats
|
||||
const rateLimiting = {
|
||||
perSenderHits: 0,
|
||||
globalHits: 0,
|
||||
};
|
||||
|
||||
// Decrypt stats
|
||||
const decrypt = {
|
||||
success: 0,
|
||||
failure: 0,
|
||||
};
|
||||
|
||||
// Memory stats (updated via gauge-style metrics)
|
||||
const memory = {
|
||||
seenTrackerSize: 0,
|
||||
rateLimiterEntries: 0,
|
||||
};
|
||||
let state = createZeroMetricsState();
|
||||
|
||||
function getOrCreateRelay(url: string) {
|
||||
let relay = relays.get(url);
|
||||
let relay = state.relays.get(url);
|
||||
if (!relay) {
|
||||
relay = {
|
||||
connects: 0,
|
||||
@@ -213,7 +222,7 @@ export function createMetrics(onMetric?: OnMetricCallback): NostrMetrics {
|
||||
circuitBreakerOpens: 0,
|
||||
circuitBreakerCloses: 0,
|
||||
};
|
||||
relays.set(url, relay);
|
||||
state.relays.set(url, relay);
|
||||
}
|
||||
return relay;
|
||||
}
|
||||
@@ -235,43 +244,43 @@ export function createMetrics(onMetric?: OnMetricCallback): NostrMetrics {
|
||||
switch (name) {
|
||||
// Event metrics
|
||||
case "event.received":
|
||||
eventsReceived += value;
|
||||
state.eventsReceived += value;
|
||||
break;
|
||||
case "event.processed":
|
||||
eventsProcessed += value;
|
||||
state.eventsProcessed += value;
|
||||
break;
|
||||
case "event.duplicate":
|
||||
eventsDuplicate += value;
|
||||
state.eventsDuplicate += value;
|
||||
break;
|
||||
case "event.rejected.invalid_shape":
|
||||
eventsRejected.invalidShape += value;
|
||||
state.eventsRejected.invalidShape += value;
|
||||
break;
|
||||
case "event.rejected.wrong_kind":
|
||||
eventsRejected.wrongKind += value;
|
||||
state.eventsRejected.wrongKind += value;
|
||||
break;
|
||||
case "event.rejected.stale":
|
||||
eventsRejected.stale += value;
|
||||
state.eventsRejected.stale += value;
|
||||
break;
|
||||
case "event.rejected.future":
|
||||
eventsRejected.future += value;
|
||||
state.eventsRejected.future += value;
|
||||
break;
|
||||
case "event.rejected.rate_limited":
|
||||
eventsRejected.rateLimited += value;
|
||||
state.eventsRejected.rateLimited += value;
|
||||
break;
|
||||
case "event.rejected.invalid_signature":
|
||||
eventsRejected.invalidSignature += value;
|
||||
state.eventsRejected.invalidSignature += value;
|
||||
break;
|
||||
case "event.rejected.oversized_ciphertext":
|
||||
eventsRejected.oversizedCiphertext += value;
|
||||
state.eventsRejected.oversizedCiphertext += value;
|
||||
break;
|
||||
case "event.rejected.oversized_plaintext":
|
||||
eventsRejected.oversizedPlaintext += value;
|
||||
state.eventsRejected.oversizedPlaintext += value;
|
||||
break;
|
||||
case "event.rejected.decrypt_failed":
|
||||
eventsRejected.decryptFailed += value;
|
||||
state.eventsRejected.decryptFailed += value;
|
||||
break;
|
||||
case "event.rejected.self_message":
|
||||
eventsRejected.selfMessage += value;
|
||||
state.eventsRejected.selfMessage += value;
|
||||
break;
|
||||
|
||||
// Relay metrics
|
||||
@@ -347,73 +356,36 @@ export function createMetrics(onMetric?: OnMetricCallback): NostrMetrics {
|
||||
|
||||
// Rate limiting
|
||||
case "rate_limit.per_sender":
|
||||
rateLimiting.perSenderHits += value;
|
||||
state.rateLimiting.perSenderHits += value;
|
||||
break;
|
||||
case "rate_limit.global":
|
||||
rateLimiting.globalHits += value;
|
||||
state.rateLimiting.globalHits += value;
|
||||
break;
|
||||
|
||||
// Decrypt
|
||||
case "decrypt.success":
|
||||
decrypt.success += value;
|
||||
state.decrypt.success += value;
|
||||
break;
|
||||
case "decrypt.failure":
|
||||
decrypt.failure += value;
|
||||
state.decrypt.failure += value;
|
||||
break;
|
||||
|
||||
// Memory (gauge-style - value replaces, not adds)
|
||||
case "memory.seen_tracker_size":
|
||||
memory.seenTrackerSize = value;
|
||||
state.memory.seenTrackerSize = value;
|
||||
break;
|
||||
case "memory.rate_limiter_entries":
|
||||
memory.rateLimiterEntries = value;
|
||||
state.memory.rateLimiterEntries = value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function getSnapshot(): MetricsSnapshot {
|
||||
// Convert relay map to object
|
||||
const relaysObj: MetricsSnapshot["relays"] = {};
|
||||
for (const [url, stats] of relays) {
|
||||
relaysObj[url] = { ...stats, messagesReceived: { ...stats.messagesReceived } };
|
||||
}
|
||||
|
||||
return {
|
||||
eventsReceived,
|
||||
eventsProcessed,
|
||||
eventsDuplicate,
|
||||
eventsRejected: { ...eventsRejected },
|
||||
relays: relaysObj,
|
||||
rateLimiting: { ...rateLimiting },
|
||||
decrypt: { ...decrypt },
|
||||
memory: { ...memory },
|
||||
snapshotAt: Date.now(),
|
||||
};
|
||||
return createMetricsSnapshot(state);
|
||||
}
|
||||
|
||||
function reset(): void {
|
||||
eventsReceived = 0;
|
||||
eventsProcessed = 0;
|
||||
eventsDuplicate = 0;
|
||||
Object.assign(eventsRejected, {
|
||||
invalidShape: 0,
|
||||
wrongKind: 0,
|
||||
stale: 0,
|
||||
future: 0,
|
||||
rateLimited: 0,
|
||||
invalidSignature: 0,
|
||||
oversizedCiphertext: 0,
|
||||
oversizedPlaintext: 0,
|
||||
decryptFailed: 0,
|
||||
selfMessage: 0,
|
||||
});
|
||||
relays.clear();
|
||||
rateLimiting.perSenderHits = 0;
|
||||
rateLimiting.globalHits = 0;
|
||||
decrypt.success = 0;
|
||||
decrypt.failure = 0;
|
||||
memory.seenTrackerSize = 0;
|
||||
memory.rateLimiterEntries = 0;
|
||||
state = createZeroMetricsState();
|
||||
}
|
||||
|
||||
return { emit, getSnapshot, reset };
|
||||
@@ -423,28 +395,7 @@ export function createMetrics(onMetric?: OnMetricCallback): NostrMetrics {
|
||||
* Create a no-op metrics instance (for when metrics are disabled).
|
||||
*/
|
||||
export function createNoopMetrics(): NostrMetrics {
|
||||
const emptySnapshot: MetricsSnapshot = {
|
||||
eventsReceived: 0,
|
||||
eventsProcessed: 0,
|
||||
eventsDuplicate: 0,
|
||||
eventsRejected: {
|
||||
invalidShape: 0,
|
||||
wrongKind: 0,
|
||||
stale: 0,
|
||||
future: 0,
|
||||
rateLimited: 0,
|
||||
invalidSignature: 0,
|
||||
oversizedCiphertext: 0,
|
||||
oversizedPlaintext: 0,
|
||||
decryptFailed: 0,
|
||||
selfMessage: 0,
|
||||
},
|
||||
relays: {},
|
||||
rateLimiting: { perSenderHits: 0, globalHits: 0 },
|
||||
decrypt: { success: 0, failure: 0 },
|
||||
memory: { seenTrackerSize: 0, rateLimiterEntries: 0 },
|
||||
snapshotAt: 0,
|
||||
};
|
||||
const emptySnapshot = createMetricsSnapshot(createZeroMetricsState(), 0);
|
||||
|
||||
return {
|
||||
emit: () => {},
|
||||
|
||||
Reference in New Issue
Block a user