package setup import ( "context" "database/sql" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/collectors" "github.com/dariuszjaniak/warp/server/internal/metrics" "github.com/dariuszjaniak/warp/server/internal/roomserver" "github.com/dariuszjaniak/warp/server/internal/syncapi" ) // The homeserver's own metrics, beside the HTTP and runtime ones every // route gets: what a dashboard (deploy/grafana) shows about a server's // rooms rather than its requests. // // They are gauges read on scrape and one counter fed from the roomserver's // emit stage, so that none of them costs anything between scrapes. // eventCounter counts persisted events from the emit stage. type eventCounter struct { events *prometheus.CounterVec } // EventPersisted implements roomserver.Notifier. func (c *eventCounter) EventPersisted(_ context.Context, n roomserver.Notification) { origin := "local" if n.Origin != "" { origin = "federation" } kind := "message" if n.Event != nil && n.Event.IsState() { kind = "state" } c.events.WithLabelValues(origin, kind).Inc() } // registerServerMetrics adds the homeserver's collectors to the registry, // and returns the event counter to put on the emit stage. func (s *Server) registerServerMetrics(notifier *syncapi.Notifier) *eventCounter { counter := &eventCounter{events: prometheus.NewCounterVec(prometheus.CounterOpts{ Namespace: metrics.Namespace, Name: "events_persisted_total", Help: "Room events persisted, by where they came from and whether they are state.", }, []string{"origin", "kind"})} s.metrics.MustRegister(counter.events) s.metrics.MustRegister(prometheus.NewGaugeFunc(prometheus.GaugeOpts{ Namespace: metrics.Namespace, Subsystem: "sync", Name: "waiting", Help: "Syncs blocked in a long poll, classic and sliding.", }, func() float64 { return float64(notifier.Listeners()) })) s.metrics.MustRegister(prometheus.NewGaugeFunc(prometheus.GaugeOpts{ Namespace: metrics.Namespace, Subsystem: "sync", Name: "sliding_connections", Help: "Sliding sync connections whose state the server is holding.", }, func() float64 { if s.sync == nil { return 0 } return float64(s.sync.SlidingConnections()) })) s.metrics.MustRegister(prometheus.NewGaugeFunc(prometheus.GaugeOpts{ Namespace: metrics.Namespace, Name: "federation_enabled", Help: "1 while federation is switched on.", }, func() float64 { if s.settings != nil && s.settings.FederationEnabled() { return 1 } return 0 })) return counter } // registerDBMetrics adds the connection pool's statistics, for a driver // that is built on database/sql. func (s *Server) registerDBMetrics() { pool, ok := s.db.(interface{ DB() *sql.DB }) if !ok { return } s.metrics.MustRegister(collectors.NewDBStatsCollector(pool.DB(), s.db.Driver())) }