use metrics::{describe_counter, describe_gauge, describe_histogram, Counter, Gauge, Histogram, Unit}; use std::sync::Arc; use uuid::Uuid; pub struct RoomMetrics { pub rooms_online: Gauge, pub users_online: Gauge, pub ws_connections_active: Gauge, pub ws_connections_total: Counter, pub ws_disconnections_total: Counter, pub messages_sent: Counter, pub messages_persisted: Counter, pub messages_persist_failed: Counter, pub broadcasts_sent: Counter, pub broadcasts_dropped: Counter, pub duplicates_skipped: Counter, pub redis_publish_failed: Counter, pub message_latency_ms: Histogram, pub ws_rate_limit_hits: Counter, pub ws_auth_failures: Counter, pub ws_heartbeat_sent_total: Counter, pub ws_heartbeat_timeout_total: Counter, pub ws_idle_timeout_total: Counter, } impl Default for RoomMetrics { fn default() -> Self { describe_gauge!("room_online_rooms", "Number of rooms with active workers"); describe_gauge!( "room_online_users", "Total number of online WebSocket users" ); describe_gauge!( "room_ws_connections_active", "Current number of active WebSocket connections" ); describe_counter!( "room_ws_connections_total", Unit::Count, "Total WebSocket connections established" ); describe_counter!( "room_ws_disconnections_total", Unit::Count, "Total WebSocket disconnections" ); describe_counter!( "room_messages_sent_total", Unit::Count, "Total messages sent to rooms" ); describe_counter!( "room_messages_persisted_total", Unit::Count, "Total messages persisted to database" ); describe_counter!( "room_messages_persist_failed_total", Unit::Count, "Total message persistence failures" ); describe_counter!( "room_broadcasts_sent_total", Unit::Count, "Total WebSocket broadcasts sent" ); describe_counter!( "room_broadcasts_dropped_total", Unit::Count, "Total broadcasts dropped due to channel full" ); describe_counter!( "room_duplicates_skipped_total", Unit::Count, "Total duplicate messages skipped (idempotency)" ); describe_counter!( "room_redis_publish_failed_total", Unit::Count, "Total Redis publish failures" ); describe_histogram!( "room_message_latency_ms", Unit::Milliseconds, "Message processing latency from publish to persist" ); describe_counter!( "room_ws_rate_limit_hits_total", Unit::Count, "Total WebSocket rate limit rejections" ); describe_counter!( "room_ws_auth_failures_total", Unit::Count, "Total WebSocket authentication/authorization failures" ); describe_counter!( "room_ws_heartbeat_sent_total", Unit::Count, "Total WebSocket heartbeat pings sent by server" ); describe_counter!( "room_ws_heartbeat_timeout_total", Unit::Count, "Total WebSocket connections closed due to heartbeat timeout" ); describe_counter!( "room_ws_idle_timeout_total", Unit::Count, "Total WebSocket connections closed due to idle timeout" ); Self { rooms_online: metrics::gauge!("room_online_rooms"), users_online: metrics::gauge!("room_online_users"), ws_connections_active: metrics::gauge!("room_ws_connections_active"), ws_connections_total: metrics::counter!("room_ws_connections_total"), ws_disconnections_total: metrics::counter!("room_ws_disconnections_total"), messages_sent: metrics::counter!("room_messages_sent_total"), messages_persisted: metrics::counter!("room_messages_persisted_total"), messages_persist_failed: metrics::counter!("room_messages_persist_failed_total"), broadcasts_sent: metrics::counter!("room_broadcasts_sent_total"), broadcasts_dropped: metrics::counter!("room_broadcasts_dropped_total"), duplicates_skipped: metrics::counter!("room_duplicates_skipped_total"), redis_publish_failed: metrics::counter!("room_redis_publish_failed_total"), message_latency_ms: metrics::histogram!("room_message_latency_ms"), ws_rate_limit_hits: metrics::counter!("room_ws_rate_limit_hits_total"), ws_auth_failures: metrics::counter!("room_ws_auth_failures_total"), ws_heartbeat_sent_total: metrics::counter!("room_ws_heartbeat_sent_total"), ws_heartbeat_timeout_total: metrics::counter!("room_ws_heartbeat_timeout_total"), ws_idle_timeout_total: metrics::counter!("room_ws_idle_timeout_total"), } } } impl RoomMetrics { pub fn new() -> Self { Self::default() } pub fn record_message_latency(&self, ms: f64) { self.message_latency_ms.record(ms); } pub fn incr_duplicates_skipped(&self) { self.duplicates_skipped.increment(1); } #[allow(dead_code)] pub async fn incr_room_connections(&self, room_id: Uuid) { let name = format!("room_connections{{room_id=\"{}\"}}", room_id); metrics::gauge!(name).increment(1.0); } #[allow(dead_code)] pub async fn dec_room_connections(&self, room_id: Uuid) { let name = format!("room_connections{{room_id=\"{}\"}}", room_id); metrics::gauge!(name).decrement(1.0); } #[allow(dead_code)] pub async fn incr_room_messages(&self, room_id: Uuid) { let name = format!("room_messages_total{{room_id=\"{}\"}}", room_id); metrics::counter!(name).increment(1); } #[allow(dead_code)] pub async fn cleanup_stale_rooms(&self, _active_room_ids: &[Uuid]) { } pub fn into_arc(self) -> Arc { Arc::new(self) } }