package sqlx import ( "database/sql" "sync" "github.com/prometheus/client_golang/prometheus" "github.com/zeromicro/go-zero/core/metric" ) const namespace = "sql_client" var ( metricReqDur = metric.NewHistogramVec(&metric.HistogramVecOpts{ Namespace: namespace, Subsystem: "requests", Name: "duration_ms", Help: "mysql client requests duration(ms).", Labels: []string{"command"}, Buckets: []float64{0.25, 0.5, 1, 1.5, 2, 3, 5, 10, 25, 50, 100, 250, 500, 1000, 2000, 5000, 10000, 15000}, }) metricReqErr = metric.NewCounterVec(&metric.CounterVecOpts{ Namespace: namespace, Subsystem: "requests", Name: "error_total", Help: "mysql client requests error count.", Labels: []string{"command", "error"}, }) metricSlowCount = metric.NewCounterVec(&metric.CounterVecOpts{ Namespace: namespace, Subsystem: "requests", Name: "slow_total", Help: "mysql client requests slow count.", Labels: []string{"command"}, }) connLabels = []string{"db_name", "hash"} connCollector = newCollector() _ prometheus.Collector = (*collector)(nil) ) type ( statGetter struct { dbName string hash string poolStats func() sql.DBStats } // collector collects statistics from a redis client. // It implements the prometheus.Collector interface. collector struct { maxOpenConnections *prometheus.Desc openConnections *prometheus.Desc inUseConnections *prometheus.Desc idleConnections *prometheus.Desc waitCount *prometheus.Desc waitDuration *prometheus.Desc maxIdleClosed *prometheus.Desc maxIdleTimeClosed *prometheus.Desc maxLifetimeClosed *prometheus.Desc clients []*statGetter lock sync.Mutex } ) func newCollector() *collector { c := &collector{ maxOpenConnections: prometheus.NewDesc( prometheus.BuildFQName(namespace, "", "max_open_connections"), "Maximum number of open connections to the database.", connLabels, nil, ), openConnections: prometheus.NewDesc( prometheus.BuildFQName(namespace, "", "open_connections"), "The number of established connections both in use and idle.", connLabels, nil, ), inUseConnections: prometheus.NewDesc( prometheus.BuildFQName(namespace, "", "in_use_connections"), "The number of connections currently in use.", connLabels, nil, ), idleConnections: prometheus.NewDesc( prometheus.BuildFQName(namespace, "", "idle_connections"), "The number of idle connections.", connLabels, nil, ), waitCount: prometheus.NewDesc( prometheus.BuildFQName(namespace, "", "wait_count_total"), "The total number of connections waited for.", connLabels, nil, ), waitDuration: prometheus.NewDesc( prometheus.BuildFQName(namespace, "", "wait_duration_seconds_total"), "The total time blocked waiting for a new connection.", connLabels, nil, ), maxIdleClosed: prometheus.NewDesc( prometheus.BuildFQName(namespace, "", "max_idle_closed_total"), "The total number of connections closed due to SetMaxIdleConns.", connLabels, nil, ), maxIdleTimeClosed: prometheus.NewDesc( prometheus.BuildFQName(namespace, "", "max_idle_time_closed_total"), "The total number of connections closed due to SetConnMaxIdleTime.", connLabels, nil, ), maxLifetimeClosed: prometheus.NewDesc( prometheus.BuildFQName(namespace, "", "max_lifetime_closed_total"), "The total number of connections closed due to SetConnMaxLifetime.", connLabels, nil, ), } prometheus.MustRegister(c) return c } // Describe implements the prometheus.Collector interface. func (c *collector) Describe(ch chan<- *prometheus.Desc) { ch <- c.maxOpenConnections ch <- c.openConnections ch <- c.inUseConnections ch <- c.idleConnections ch <- c.waitCount ch <- c.waitDuration ch <- c.maxIdleClosed ch <- c.maxLifetimeClosed ch <- c.maxIdleTimeClosed } // Collect implements the prometheus.Collector interface. func (c *collector) Collect(ch chan<- prometheus.Metric) { c.lock.Lock() defer c.lock.Unlock() for _, client := range c.clients { dbName, hash := client.dbName, client.hash stats := client.poolStats() ch <- prometheus.MustNewConstMetric(c.maxOpenConnections, prometheus.GaugeValue, float64(stats.MaxOpenConnections), dbName, hash) ch <- prometheus.MustNewConstMetric(c.openConnections, prometheus.GaugeValue, float64(stats.OpenConnections), dbName, hash) ch <- prometheus.MustNewConstMetric(c.inUseConnections, prometheus.GaugeValue, float64(stats.InUse), dbName, hash) ch <- prometheus.MustNewConstMetric(c.idleConnections, prometheus.GaugeValue, float64(stats.Idle), dbName, hash) ch <- prometheus.MustNewConstMetric(c.waitCount, prometheus.CounterValue, float64(stats.WaitCount), dbName, hash) ch <- prometheus.MustNewConstMetric(c.waitDuration, prometheus.CounterValue, stats.WaitDuration.Seconds(), dbName, hash) ch <- prometheus.MustNewConstMetric(c.maxIdleClosed, prometheus.CounterValue, float64(stats.MaxIdleClosed), dbName, hash) ch <- prometheus.MustNewConstMetric(c.maxLifetimeClosed, prometheus.CounterValue, float64(stats.MaxLifetimeClosed), dbName, hash) ch <- prometheus.MustNewConstMetric(c.maxIdleTimeClosed, prometheus.CounterValue, float64(stats.MaxIdleTimeClosed), dbName, hash) } } func (c *collector) registerClient(client *statGetter) { c.lock.Lock() defer c.lock.Unlock() c.clients = append(c.clients, client) }