github.com/letsencrypt/boulder@v0.20251208.0/sa/metrics.go (about) 1 package sa 2 3 import ( 4 "database/sql" 5 6 "github.com/prometheus/client_golang/prometheus" 7 ) 8 9 type dbMetricsCollector struct { 10 db *sql.DB 11 dbSettings DbSettings 12 13 maxOpenConns *prometheus.Desc 14 maxIdleConns *prometheus.Desc 15 connMaxLifetime *prometheus.Desc 16 connMaxIdleTime *prometheus.Desc 17 openConns *prometheus.Desc 18 inUse *prometheus.Desc 19 idle *prometheus.Desc 20 waitCount *prometheus.Desc 21 waitDuration *prometheus.Desc 22 maxIdleClosed *prometheus.Desc 23 maxLifetimeClosed *prometheus.Desc 24 } 25 26 // Describe is implemented with DescribeByCollect. That's possible because the 27 // Collect method will always return the same metrics with the same descriptors. 28 func (dbc dbMetricsCollector) Describe(ch chan<- *prometheus.Desc) { 29 prometheus.DescribeByCollect(dbc, ch) 30 } 31 32 // Collect first triggers the dbMaps's sql.Db's Stats function. Then it 33 // creates constant metrics for each DBStats value on the fly based on the 34 // returned data. 35 // 36 // Note that Collect could be called concurrently, so we depend on 37 // Stats() to be concurrency-safe. 38 func (dbc dbMetricsCollector) Collect(ch chan<- prometheus.Metric) { 39 writeStat := func(stat *prometheus.Desc, typ prometheus.ValueType, val float64) { 40 ch <- prometheus.MustNewConstMetric(stat, typ, val) 41 } 42 writeCounter := func(stat *prometheus.Desc, val float64) { 43 writeStat(stat, prometheus.CounterValue, val) 44 } 45 writeGauge := func(stat *prometheus.Desc, val float64) { 46 writeStat(stat, prometheus.GaugeValue, val) 47 } 48 49 // Translate the DBMap's db.DBStats counter values into Prometheus metrics. 50 dbMapStats := dbc.db.Stats() 51 writeGauge(dbc.maxOpenConns, float64(dbMapStats.MaxOpenConnections)) 52 writeGauge(dbc.maxIdleConns, float64(dbc.dbSettings.MaxIdleConns)) 53 writeGauge(dbc.connMaxLifetime, float64(dbc.dbSettings.ConnMaxLifetime)) 54 writeGauge(dbc.connMaxIdleTime, float64(dbc.dbSettings.ConnMaxIdleTime)) 55 writeGauge(dbc.openConns, float64(dbMapStats.OpenConnections)) 56 writeGauge(dbc.inUse, float64(dbMapStats.InUse)) 57 writeGauge(dbc.idle, float64(dbMapStats.Idle)) 58 writeCounter(dbc.waitCount, float64(dbMapStats.WaitCount)) 59 writeCounter(dbc.waitDuration, dbMapStats.WaitDuration.Seconds()) 60 writeCounter(dbc.maxIdleClosed, float64(dbMapStats.MaxIdleClosed)) 61 writeCounter(dbc.maxLifetimeClosed, float64(dbMapStats.MaxLifetimeClosed)) 62 } 63 64 // initDBMetrics will register a Collector that translates the provided dbMap's 65 // stats and DbSettings into Prometheus metrics on the fly. The exported metrics 66 // all start with `db_`. The underlying data comes from sql.DBStats: 67 // https://pkg.go.dev/database/sql#DBStats 68 func initDBMetrics(db *sql.DB, stats prometheus.Registerer, dbSettings DbSettings, address string, user string) error { 69 // Create a dbMetricsCollector and register it 70 dbc := dbMetricsCollector{db: db, dbSettings: dbSettings} 71 72 labels := prometheus.Labels{"address": address, "user": user} 73 74 dbc.maxOpenConns = prometheus.NewDesc( 75 "db_max_open_connections", 76 "Maximum number of DB connections allowed.", 77 nil, labels) 78 79 dbc.maxIdleConns = prometheus.NewDesc( 80 "db_max_idle_connections", 81 "Maximum number of idle DB connections allowed.", 82 nil, labels) 83 84 dbc.connMaxLifetime = prometheus.NewDesc( 85 "db_connection_max_lifetime", 86 "Maximum lifetime of DB connections allowed.", 87 nil, labels) 88 89 dbc.connMaxIdleTime = prometheus.NewDesc( 90 "db_connection_max_idle_time", 91 "Maximum lifetime of idle DB connections allowed.", 92 nil, labels) 93 94 dbc.openConns = prometheus.NewDesc( 95 "db_open_connections", 96 "Number of established DB connections (in-use and idle).", 97 nil, labels) 98 99 dbc.inUse = prometheus.NewDesc( 100 "db_inuse", 101 "Number of DB connections currently in use.", 102 nil, labels) 103 104 dbc.idle = prometheus.NewDesc( 105 "db_idle", 106 "Number of idle DB connections.", 107 nil, labels) 108 109 dbc.waitCount = prometheus.NewDesc( 110 "db_wait_count", 111 "Total number of DB connections waited for.", 112 nil, labels) 113 114 dbc.waitDuration = prometheus.NewDesc( 115 "db_wait_duration_seconds", 116 "The total time blocked waiting for a new connection.", 117 nil, labels) 118 119 dbc.maxIdleClosed = prometheus.NewDesc( 120 "db_max_idle_closed", 121 "Total number of connections closed due to SetMaxIdleConns.", 122 nil, labels) 123 124 dbc.maxLifetimeClosed = prometheus.NewDesc( 125 "db_max_lifetime_closed", 126 "Total number of connections closed due to SetConnMaxLifetime.", 127 nil, labels) 128 129 return stats.Register(dbc) 130 }