81 lines
3.4 KiB
Go
81 lines
3.4 KiB
Go
package metrics
|
|
|
|
import (
|
|
"database/sql"
|
|
"strings"
|
|
"sync"
|
|
|
|
"github.com/prometheus/client_golang/prometheus"
|
|
)
|
|
|
|
type dbStatsProvider interface {
|
|
Stats() sql.DBStats
|
|
}
|
|
|
|
type dbPoolCollector struct {
|
|
pools sync.Map
|
|
desc map[string]*prometheus.Desc
|
|
}
|
|
|
|
func newDBPoolCollector() *dbPoolCollector {
|
|
labels := []string{"pod", "pool"}
|
|
return &dbPoolCollector{desc: map[string]*prometheus.Desc{
|
|
"max_open": prometheus.NewDesc("im_db_pool_max_open_connections", "Configured maximum open database connections.", labels, nil),
|
|
"open": prometheus.NewDesc("im_db_pool_open_connections", "Current open database connections.", labels, nil),
|
|
"in_use": prometheus.NewDesc("im_db_pool_in_use_connections", "Current database connections in use.", labels, nil),
|
|
"idle": prometheus.NewDesc("im_db_pool_idle_connections", "Current idle database connections.", labels, nil),
|
|
"wait": prometheus.NewDesc("im_db_pool_wait_total", "Total waits for a database connection.", labels, nil),
|
|
"wait_duration": prometheus.NewDesc("im_db_pool_wait_duration_seconds_total", "Total time spent waiting for database connections.", labels, nil),
|
|
"max_idle_closed": prometheus.NewDesc("im_db_pool_max_idle_closed_total", "Connections closed because the idle limit was exceeded.", labels, nil),
|
|
"max_lifetime_closed": prometheus.NewDesc("im_db_pool_max_lifetime_closed_total", "Connections closed because their lifetime expired.", labels, nil),
|
|
}}
|
|
}
|
|
|
|
func (c *dbPoolCollector) Describe(ch chan<- *prometheus.Desc) {
|
|
for _, desc := range c.desc {
|
|
ch <- desc
|
|
}
|
|
}
|
|
|
|
func (c *dbPoolCollector) Collect(ch chan<- prometheus.Metric) {
|
|
pod := metricPodName()
|
|
c.pools.Range(func(key, value any) bool {
|
|
pool, ok := key.(string)
|
|
provider, providerOK := value.(dbStatsProvider)
|
|
if !ok || !providerOK {
|
|
return true
|
|
}
|
|
stats := provider.Stats()
|
|
labels := []string{pod, pool}
|
|
ch <- prometheus.MustNewConstMetric(c.desc["max_open"], prometheus.GaugeValue, float64(stats.MaxOpenConnections), labels...)
|
|
ch <- prometheus.MustNewConstMetric(c.desc["open"], prometheus.GaugeValue, float64(stats.OpenConnections), labels...)
|
|
ch <- prometheus.MustNewConstMetric(c.desc["in_use"], prometheus.GaugeValue, float64(stats.InUse), labels...)
|
|
ch <- prometheus.MustNewConstMetric(c.desc["idle"], prometheus.GaugeValue, float64(stats.Idle), labels...)
|
|
ch <- prometheus.MustNewConstMetric(c.desc["wait"], prometheus.CounterValue, float64(stats.WaitCount), labels...)
|
|
ch <- prometheus.MustNewConstMetric(c.desc["wait_duration"], prometheus.CounterValue, stats.WaitDuration.Seconds(), labels...)
|
|
ch <- prometheus.MustNewConstMetric(c.desc["max_idle_closed"], prometheus.CounterValue, float64(stats.MaxIdleClosed), labels...)
|
|
ch <- prometheus.MustNewConstMetric(c.desc["max_lifetime_closed"], prometheus.CounterValue, float64(stats.MaxLifetimeClosed), labels...)
|
|
return true
|
|
})
|
|
}
|
|
|
|
var dbPools = newDBPoolCollector()
|
|
|
|
// RegisterDBPool exposes database/sql pool pressure for one logical database.
|
|
// Registering the same name again replaces the provider, which keeps tests and
|
|
// adapter reopen operations safe.
|
|
func RegisterDBPool(name string, provider dbStatsProvider) {
|
|
if provider == nil {
|
|
return
|
|
}
|
|
name = strings.TrimSpace(name)
|
|
if name == "" {
|
|
name = "default"
|
|
}
|
|
dbPools.pools.Store(name, provider)
|
|
}
|
|
|
|
func init() {
|
|
Registry.MustRegister(dbPools)
|
|
}
|