From c0be446b4ff0935c8f34de46fbd8890d9c46a774 Mon Sep 17 00:00:00 2001 From: Mitsuo Heijo Date: Wed, 12 May 2021 20:39:53 +0900 Subject: [PATCH 1/2] Add collector for database/sql#DBStats Signed-off-by: Mitsuo Heijo --- prometheus/collectors/dbstats_collector.go | 137 ++++++++++++++++++ .../collectors/dbstats_collector_go115.go | 129 +++++++++++++++++ .../collectors/dbstats_collector_test.go | 71 +++++++++ 3 files changed, 337 insertions(+) create mode 100644 prometheus/collectors/dbstats_collector.go create mode 100644 prometheus/collectors/dbstats_collector_go115.go create mode 100644 prometheus/collectors/dbstats_collector_test.go diff --git a/prometheus/collectors/dbstats_collector.go b/prometheus/collectors/dbstats_collector.go new file mode 100644 index 000000000..4e10eb73b --- /dev/null +++ b/prometheus/collectors/dbstats_collector.go @@ -0,0 +1,137 @@ +// Copyright 2021 The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:build go1.15 +// +build go1.15 + +package collectors + +import ( + "database/sql" + + "github.com/prometheus/client_golang/prometheus" +) + +type dbStatsCollector struct { + db *sql.DB + + 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 +} + +// DBStatsCollectorOpts defines the behavior of a db stats collector +// created with NewDBStatsCollector. +type DBStatsCollectorOpts struct { + // DriverName holds the name of driver. + // It will not used for empty strings. + DriverName string +} + +// NewDBStatsCollector returns a collector that exports metrics about the given *sql.DB. +// See https://golang.org/pkg/database/sql/#DBStats for more information on stats. +func NewDBStatsCollector(db *sql.DB, opts DBStatsCollectorOpts) prometheus.Collector { + var fqName func(name string) string + if opts.DriverName == "" { + fqName = func(name string) string { + return "go_db_stats_" + name + } + } else { + fqName = func(name string) string { + return "go_" + opts.DriverName + "_db_stats_" + name + } + } + return &dbStatsCollector{ + db: db, + maxOpenConnections: prometheus.NewDesc( + fqName("max_open_connections"), + "Maximum number of open connections to the database.", + nil, nil, + ), + openConnections: prometheus.NewDesc( + fqName("open_connections"), + "The number of established connections both in use and idle.", + nil, nil, + ), + inUseConnections: prometheus.NewDesc( + fqName("in_use_connections"), + "The number of connections currently in use.", + nil, nil, + ), + idleConnections: prometheus.NewDesc( + fqName("idle_connections"), + "The number of idle connections.", + nil, nil, + ), + waitCount: prometheus.NewDesc( + fqName("wait_count_total"), + "The total number of connections waited for.", + nil, nil, + ), + waitDuration: prometheus.NewDesc( + fqName("wait_duration_seconds_total"), + "The total time blocked waiting for a new connection.", + nil, nil, + ), + maxIdleClosed: prometheus.NewDesc( + fqName("max_idle_closed_total"), + "The total number of connections closed due to SetMaxIdleConns.", + nil, nil, + ), + maxIdleTimeClosed: prometheus.NewDesc( + fqName("max_idle_time_closed_total"), + "The total number of connections closed due to SetConnMaxIdleTime.", + nil, nil, + ), + maxLifetimeClosed: prometheus.NewDesc( + fqName("max_lifetime_closed_total"), + "The total number of connections closed due to SetConnMaxLifetime.", + nil, nil, + ), + } +} + +// Describe implements Collector. +func (c *dbStatsCollector) 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.maxIdleTimeClosed + ch <- c.maxLifetimeClosed +} + +// Collect implements Collector. +func (c *dbStatsCollector) Collect(ch chan<- prometheus.Metric) { + stats := c.db.Stats() + ch <- prometheus.MustNewConstMetric(c.maxOpenConnections, prometheus.GaugeValue, float64(stats.MaxOpenConnections)) + ch <- prometheus.MustNewConstMetric(c.openConnections, prometheus.GaugeValue, float64(stats.OpenConnections)) + ch <- prometheus.MustNewConstMetric(c.inUseConnections, prometheus.GaugeValue, float64(stats.InUse)) + ch <- prometheus.MustNewConstMetric(c.idleConnections, prometheus.GaugeValue, float64(stats.Idle)) + ch <- prometheus.MustNewConstMetric(c.waitCount, prometheus.CounterValue, float64(stats.WaitCount)) + ch <- prometheus.MustNewConstMetric(c.waitDuration, prometheus.CounterValue, stats.WaitDuration.Seconds()) + ch <- prometheus.MustNewConstMetric(c.maxIdleClosed, prometheus.CounterValue, float64(stats.MaxIdleClosed)) + ch <- prometheus.MustNewConstMetric(c.maxIdleTimeClosed, prometheus.CounterValue, float64(stats.MaxIdleTimeClosed)) + ch <- prometheus.MustNewConstMetric(c.maxLifetimeClosed, prometheus.CounterValue, float64(stats.MaxLifetimeClosed)) +} diff --git a/prometheus/collectors/dbstats_collector_go115.go b/prometheus/collectors/dbstats_collector_go115.go new file mode 100644 index 000000000..ed482acbd --- /dev/null +++ b/prometheus/collectors/dbstats_collector_go115.go @@ -0,0 +1,129 @@ +// Copyright 2021 The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:build !go1.15 +// +build !go1.15 + +package collectors + +import ( + "database/sql" + + "github.com/prometheus/client_golang/prometheus" +) + +type dbStatsCollector struct { + db *sql.DB + + maxOpenConnections *prometheus.Desc + + openConnections *prometheus.Desc + inUseConnections *prometheus.Desc + idleConnections *prometheus.Desc + + waitCount *prometheus.Desc + waitDuration *prometheus.Desc + maxIdleClosed *prometheus.Desc + maxLifetimeClosed *prometheus.Desc +} + +// DBStatsCollectorOpts defines the behavior of a db stats collector +// created with NewDBStatsCollector. +type DBStatsCollectorOpts struct { + // DriverName holds the name of driver. + // It will not used for empty strings. + DriverName string +} + +// NewDBStatsCollector returns a collector that exports metrics about the given *sql.DB. +// See https://golang.org/pkg/database/sql/#DBStats for more information on stats. +func NewDBStatsCollector(db *sql.DB, opts DBStatsCollectorOpts) prometheus.Collector { + var fqName func(name string) string + if opts.DriverName == "" { + fqName = func(name string) string { + return "go_db_stats_" + name + } + } else { + fqName = func(name string) string { + return "go_" + opts.DriverName + "_db_stats_" + name + } + } + return &dbStatsCollector{ + db: db, + maxOpenConnections: prometheus.NewDesc( + fqName("max_open_connections"), + "Maximum number of open connections to the database.", + nil, nil, + ), + openConnections: prometheus.NewDesc( + fqName("open_connections"), + "The number of established connections both in use and idle.", + nil, nil, + ), + inUseConnections: prometheus.NewDesc( + fqName("in_use_connections"), + "The number of connections currently in use.", + nil, nil, + ), + idleConnections: prometheus.NewDesc( + fqName("idle_connections"), + "The number of idle connections.", + nil, nil, + ), + waitCount: prometheus.NewDesc( + fqName("wait_count_total"), + "The total number of connections waited for.", + nil, nil, + ), + waitDuration: prometheus.NewDesc( + fqName("wait_duration_seconds_total"), + "The total time blocked waiting for a new connection.", + nil, nil, + ), + maxIdleClosed: prometheus.NewDesc( + fqName("max_idle_closed_total"), + "The total number of connections closed due to SetMaxIdleConns.", + nil, nil, + ), + maxLifetimeClosed: prometheus.NewDesc( + fqName("max_lifetime_closed_total"), + "The total number of connections closed due to SetConnMaxLifetime.", + nil, nil, + ), + } +} + +// Describe implements Collector. +func (c *dbStatsCollector) 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 +} + +// Collect implements Collector. +func (c *dbStatsCollector) Collect(ch chan<- prometheus.Metric) { + stats := c.db.Stats() + ch <- prometheus.MustNewConstMetric(c.maxOpenConnections, prometheus.GaugeValue, float64(stats.MaxOpenConnections)) + ch <- prometheus.MustNewConstMetric(c.openConnections, prometheus.GaugeValue, float64(stats.OpenConnections)) + ch <- prometheus.MustNewConstMetric(c.inUseConnections, prometheus.GaugeValue, float64(stats.InUse)) + ch <- prometheus.MustNewConstMetric(c.idleConnections, prometheus.GaugeValue, float64(stats.Idle)) + ch <- prometheus.MustNewConstMetric(c.waitCount, prometheus.CounterValue, float64(stats.WaitCount)) + ch <- prometheus.MustNewConstMetric(c.waitDuration, prometheus.CounterValue, stats.WaitDuration.Seconds()) + ch <- prometheus.MustNewConstMetric(c.maxIdleClosed, prometheus.CounterValue, float64(stats.MaxIdleClosed)) + ch <- prometheus.MustNewConstMetric(c.maxLifetimeClosed, prometheus.CounterValue, float64(stats.MaxLifetimeClosed)) +} diff --git a/prometheus/collectors/dbstats_collector_test.go b/prometheus/collectors/dbstats_collector_test.go new file mode 100644 index 000000000..2d83c964a --- /dev/null +++ b/prometheus/collectors/dbstats_collector_test.go @@ -0,0 +1,71 @@ +// Copyright 2021 The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package collectors + +import ( + "database/sql" + "runtime" + "testing" + + "github.com/prometheus/client_golang/prometheus" +) + +func TestDBStatsCollector(t *testing.T) { + reg := prometheus.NewRegistry() + db := new(sql.DB) + opts := DBStatsCollectorOpts{DriverName: "test"} + if err := reg.Register(NewDBStatsCollector(db, opts)); err != nil { + t.Fatal(err) + } + + mfs, err := reg.Gather() + if err != nil { + t.Fatal(err) + } + + names := []string{ + "go_test_db_stats_max_open_connections", + "go_test_db_stats_open_connections", + "go_test_db_stats_in_use_connections", + "go_test_db_stats_idle_connections", + "go_test_db_stats_wait_count_total", + "go_test_db_stats_wait_duration_seconds_total", + "go_test_db_stats_max_idle_closed_total", + "go_test_db_stats_max_lifetime_closed_total", + } + if runtime.Version() >= "go1.15" { + names = append(names, "go_test_db_stats_max_idle_time_closed_total") + } + type result struct { + found bool + } + results := make(map[string]result) + for _, name := range names { + results[name] = result{found: false} + } + for _, mf := range mfs { + for _, name := range names { + if name == mf.GetName() { + results[name] = result{found: true} + break + } + } + } + + for name, result := range results { + if !result.found { + t.Errorf("%s not found", name) + } + } +} From c20bd79c1e6b1d1298f3b6433172790ce7bf5c67 Mon Sep 17 00:00:00 2001 From: Mitsuo Heijo Date: Thu, 20 May 2021 02:01:59 +0900 Subject: [PATCH 2/2] improve metrics structure Signed-off-by: Mitsuo Heijo --- prometheus/collectors/dbstats_collector.go | 63 +++--------- .../collectors/dbstats_collector_go115.go | 98 ++----------------- .../collectors/dbstats_collector_pre_go115.go | 41 ++++++++ .../collectors/dbstats_collector_test.go | 52 +++++++--- 4 files changed, 100 insertions(+), 154 deletions(-) create mode 100644 prometheus/collectors/dbstats_collector_pre_go115.go diff --git a/prometheus/collectors/dbstats_collector.go b/prometheus/collectors/dbstats_collector.go index 4e10eb73b..d7a37ebe1 100644 --- a/prometheus/collectors/dbstats_collector.go +++ b/prometheus/collectors/dbstats_collector.go @@ -11,9 +11,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -//go:build go1.15 -// +build go1.15 - package collectors import ( @@ -38,100 +35,68 @@ type dbStatsCollector struct { maxLifetimeClosed *prometheus.Desc } -// DBStatsCollectorOpts defines the behavior of a db stats collector -// created with NewDBStatsCollector. -type DBStatsCollectorOpts struct { - // DriverName holds the name of driver. - // It will not used for empty strings. - DriverName string -} - // NewDBStatsCollector returns a collector that exports metrics about the given *sql.DB. // See https://golang.org/pkg/database/sql/#DBStats for more information on stats. -func NewDBStatsCollector(db *sql.DB, opts DBStatsCollectorOpts) prometheus.Collector { - var fqName func(name string) string - if opts.DriverName == "" { - fqName = func(name string) string { - return "go_db_stats_" + name - } - } else { - fqName = func(name string) string { - return "go_" + opts.DriverName + "_db_stats_" + name - } +func NewDBStatsCollector(db *sql.DB, dbName string) prometheus.Collector { + fqName := func(name string) string { + return "go_sql_" + name } return &dbStatsCollector{ db: db, maxOpenConnections: prometheus.NewDesc( fqName("max_open_connections"), "Maximum number of open connections to the database.", - nil, nil, + nil, prometheus.Labels{"db_name": dbName}, ), openConnections: prometheus.NewDesc( fqName("open_connections"), "The number of established connections both in use and idle.", - nil, nil, + nil, prometheus.Labels{"db_name": dbName}, ), inUseConnections: prometheus.NewDesc( fqName("in_use_connections"), "The number of connections currently in use.", - nil, nil, + nil, prometheus.Labels{"db_name": dbName}, ), idleConnections: prometheus.NewDesc( fqName("idle_connections"), "The number of idle connections.", - nil, nil, + nil, prometheus.Labels{"db_name": dbName}, ), waitCount: prometheus.NewDesc( fqName("wait_count_total"), "The total number of connections waited for.", - nil, nil, + nil, prometheus.Labels{"db_name": dbName}, ), waitDuration: prometheus.NewDesc( fqName("wait_duration_seconds_total"), "The total time blocked waiting for a new connection.", - nil, nil, + nil, prometheus.Labels{"db_name": dbName}, ), maxIdleClosed: prometheus.NewDesc( fqName("max_idle_closed_total"), "The total number of connections closed due to SetMaxIdleConns.", - nil, nil, + nil, prometheus.Labels{"db_name": dbName}, ), maxIdleTimeClosed: prometheus.NewDesc( fqName("max_idle_time_closed_total"), "The total number of connections closed due to SetConnMaxIdleTime.", - nil, nil, + nil, prometheus.Labels{"db_name": dbName}, ), maxLifetimeClosed: prometheus.NewDesc( fqName("max_lifetime_closed_total"), "The total number of connections closed due to SetConnMaxLifetime.", - nil, nil, + nil, prometheus.Labels{"db_name": dbName}, ), } } // Describe implements Collector. func (c *dbStatsCollector) 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.maxIdleTimeClosed - ch <- c.maxLifetimeClosed + c.describe(ch) } // Collect implements Collector. func (c *dbStatsCollector) Collect(ch chan<- prometheus.Metric) { - stats := c.db.Stats() - ch <- prometheus.MustNewConstMetric(c.maxOpenConnections, prometheus.GaugeValue, float64(stats.MaxOpenConnections)) - ch <- prometheus.MustNewConstMetric(c.openConnections, prometheus.GaugeValue, float64(stats.OpenConnections)) - ch <- prometheus.MustNewConstMetric(c.inUseConnections, prometheus.GaugeValue, float64(stats.InUse)) - ch <- prometheus.MustNewConstMetric(c.idleConnections, prometheus.GaugeValue, float64(stats.Idle)) - ch <- prometheus.MustNewConstMetric(c.waitCount, prometheus.CounterValue, float64(stats.WaitCount)) - ch <- prometheus.MustNewConstMetric(c.waitDuration, prometheus.CounterValue, stats.WaitDuration.Seconds()) - ch <- prometheus.MustNewConstMetric(c.maxIdleClosed, prometheus.CounterValue, float64(stats.MaxIdleClosed)) - ch <- prometheus.MustNewConstMetric(c.maxIdleTimeClosed, prometheus.CounterValue, float64(stats.MaxIdleTimeClosed)) - ch <- prometheus.MustNewConstMetric(c.maxLifetimeClosed, prometheus.CounterValue, float64(stats.MaxLifetimeClosed)) + c.collect(ch) } diff --git a/prometheus/collectors/dbstats_collector_go115.go b/prometheus/collectors/dbstats_collector_go115.go index ed482acbd..c5324c64a 100644 --- a/prometheus/collectors/dbstats_collector_go115.go +++ b/prometheus/collectors/dbstats_collector_go115.go @@ -11,100 +11,13 @@ // See the License for the specific language governing permissions and // limitations under the License. -//go:build !go1.15 -// +build !go1.15 +// +build go1.15 package collectors -import ( - "database/sql" +import "github.com/prometheus/client_golang/prometheus" - "github.com/prometheus/client_golang/prometheus" -) - -type dbStatsCollector struct { - db *sql.DB - - maxOpenConnections *prometheus.Desc - - openConnections *prometheus.Desc - inUseConnections *prometheus.Desc - idleConnections *prometheus.Desc - - waitCount *prometheus.Desc - waitDuration *prometheus.Desc - maxIdleClosed *prometheus.Desc - maxLifetimeClosed *prometheus.Desc -} - -// DBStatsCollectorOpts defines the behavior of a db stats collector -// created with NewDBStatsCollector. -type DBStatsCollectorOpts struct { - // DriverName holds the name of driver. - // It will not used for empty strings. - DriverName string -} - -// NewDBStatsCollector returns a collector that exports metrics about the given *sql.DB. -// See https://golang.org/pkg/database/sql/#DBStats for more information on stats. -func NewDBStatsCollector(db *sql.DB, opts DBStatsCollectorOpts) prometheus.Collector { - var fqName func(name string) string - if opts.DriverName == "" { - fqName = func(name string) string { - return "go_db_stats_" + name - } - } else { - fqName = func(name string) string { - return "go_" + opts.DriverName + "_db_stats_" + name - } - } - return &dbStatsCollector{ - db: db, - maxOpenConnections: prometheus.NewDesc( - fqName("max_open_connections"), - "Maximum number of open connections to the database.", - nil, nil, - ), - openConnections: prometheus.NewDesc( - fqName("open_connections"), - "The number of established connections both in use and idle.", - nil, nil, - ), - inUseConnections: prometheus.NewDesc( - fqName("in_use_connections"), - "The number of connections currently in use.", - nil, nil, - ), - idleConnections: prometheus.NewDesc( - fqName("idle_connections"), - "The number of idle connections.", - nil, nil, - ), - waitCount: prometheus.NewDesc( - fqName("wait_count_total"), - "The total number of connections waited for.", - nil, nil, - ), - waitDuration: prometheus.NewDesc( - fqName("wait_duration_seconds_total"), - "The total time blocked waiting for a new connection.", - nil, nil, - ), - maxIdleClosed: prometheus.NewDesc( - fqName("max_idle_closed_total"), - "The total number of connections closed due to SetMaxIdleConns.", - nil, nil, - ), - maxLifetimeClosed: prometheus.NewDesc( - fqName("max_lifetime_closed_total"), - "The total number of connections closed due to SetConnMaxLifetime.", - nil, nil, - ), - } -} - -// Describe implements Collector. -func (c *dbStatsCollector) Describe(ch chan<- *prometheus.Desc) { +func (c *dbStatsCollector) describe(ch chan<- *prometheus.Desc) { ch <- c.maxOpenConnections ch <- c.openConnections ch <- c.inUseConnections @@ -112,11 +25,11 @@ func (c *dbStatsCollector) Describe(ch chan<- *prometheus.Desc) { ch <- c.waitCount ch <- c.waitDuration ch <- c.maxIdleClosed + ch <- c.maxIdleTimeClosed ch <- c.maxLifetimeClosed } -// Collect implements Collector. -func (c *dbStatsCollector) Collect(ch chan<- prometheus.Metric) { +func (c *dbStatsCollector) collect(ch chan<- prometheus.Metric) { stats := c.db.Stats() ch <- prometheus.MustNewConstMetric(c.maxOpenConnections, prometheus.GaugeValue, float64(stats.MaxOpenConnections)) ch <- prometheus.MustNewConstMetric(c.openConnections, prometheus.GaugeValue, float64(stats.OpenConnections)) @@ -125,5 +38,6 @@ func (c *dbStatsCollector) Collect(ch chan<- prometheus.Metric) { ch <- prometheus.MustNewConstMetric(c.waitCount, prometheus.CounterValue, float64(stats.WaitCount)) ch <- prometheus.MustNewConstMetric(c.waitDuration, prometheus.CounterValue, stats.WaitDuration.Seconds()) ch <- prometheus.MustNewConstMetric(c.maxIdleClosed, prometheus.CounterValue, float64(stats.MaxIdleClosed)) + ch <- prometheus.MustNewConstMetric(c.maxIdleTimeClosed, prometheus.CounterValue, float64(stats.MaxIdleTimeClosed)) ch <- prometheus.MustNewConstMetric(c.maxLifetimeClosed, prometheus.CounterValue, float64(stats.MaxLifetimeClosed)) } diff --git a/prometheus/collectors/dbstats_collector_pre_go115.go b/prometheus/collectors/dbstats_collector_pre_go115.go new file mode 100644 index 000000000..8eb5153ff --- /dev/null +++ b/prometheus/collectors/dbstats_collector_pre_go115.go @@ -0,0 +1,41 @@ +// Copyright 2021 The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// +build !go1.15 + +package collectors + +import "github.com/prometheus/client_golang/prometheus" + +func (c *dbStatsCollector) 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 +} + +func (c *dbStatsCollector) collect(ch chan<- prometheus.Metric) { + stats := c.db.Stats() + ch <- prometheus.MustNewConstMetric(c.maxOpenConnections, prometheus.GaugeValue, float64(stats.MaxOpenConnections)) + ch <- prometheus.MustNewConstMetric(c.openConnections, prometheus.GaugeValue, float64(stats.OpenConnections)) + ch <- prometheus.MustNewConstMetric(c.inUseConnections, prometheus.GaugeValue, float64(stats.InUse)) + ch <- prometheus.MustNewConstMetric(c.idleConnections, prometheus.GaugeValue, float64(stats.Idle)) + ch <- prometheus.MustNewConstMetric(c.waitCount, prometheus.CounterValue, float64(stats.WaitCount)) + ch <- prometheus.MustNewConstMetric(c.waitDuration, prometheus.CounterValue, stats.WaitDuration.Seconds()) + ch <- prometheus.MustNewConstMetric(c.maxIdleClosed, prometheus.CounterValue, float64(stats.MaxIdleClosed)) + ch <- prometheus.MustNewConstMetric(c.maxLifetimeClosed, prometheus.CounterValue, float64(stats.MaxLifetimeClosed)) +} diff --git a/prometheus/collectors/dbstats_collector_test.go b/prometheus/collectors/dbstats_collector_test.go index 2d83c964a..4cfdda541 100644 --- a/prometheus/collectors/dbstats_collector_test.go +++ b/prometheus/collectors/dbstats_collector_test.go @@ -23,10 +23,17 @@ import ( func TestDBStatsCollector(t *testing.T) { reg := prometheus.NewRegistry() - db := new(sql.DB) - opts := DBStatsCollectorOpts{DriverName: "test"} - if err := reg.Register(NewDBStatsCollector(db, opts)); err != nil { - t.Fatal(err) + { + db := new(sql.DB) + if err := reg.Register(NewDBStatsCollector(db, "db_A")); err != nil { + t.Fatal(err) + } + } + { + db := new(sql.DB) + if err := reg.Register(NewDBStatsCollector(db, "db_B")); err != nil { + t.Fatal(err) + } } mfs, err := reg.Gather() @@ -35,17 +42,17 @@ func TestDBStatsCollector(t *testing.T) { } names := []string{ - "go_test_db_stats_max_open_connections", - "go_test_db_stats_open_connections", - "go_test_db_stats_in_use_connections", - "go_test_db_stats_idle_connections", - "go_test_db_stats_wait_count_total", - "go_test_db_stats_wait_duration_seconds_total", - "go_test_db_stats_max_idle_closed_total", - "go_test_db_stats_max_lifetime_closed_total", + "go_sql_max_open_connections", + "go_sql_open_connections", + "go_sql_in_use_connections", + "go_sql_idle_connections", + "go_sql_wait_count_total", + "go_sql_wait_duration_seconds_total", + "go_sql_max_idle_closed_total", + "go_sql_max_lifetime_closed_total", } if runtime.Version() >= "go1.15" { - names = append(names, "go_test_db_stats_max_idle_time_closed_total") + names = append(names, "go_sql_max_idle_time_closed_total") } type result struct { found bool @@ -55,6 +62,25 @@ func TestDBStatsCollector(t *testing.T) { results[name] = result{found: false} } for _, mf := range mfs { + m := mf.GetMetric() + if len(m) != 2 { + t.Errorf("expected 2 metrics bug got %d", len(m)) + } + labelA := m[0].GetLabel()[0] + if name := labelA.GetName(); name != "db_name" { + t.Errorf("expected to get label \"db_name\" but got %s", name) + } + if value := labelA.GetValue(); value != "db_A" { + t.Errorf("expected to get value \"db_A\" but got %s", value) + } + labelB := m[1].GetLabel()[0] + if name := labelB.GetName(); name != "db_name" { + t.Errorf("expected to get label \"db_name\" but got %s", name) + } + if value := labelB.GetValue(); value != "db_B" { + t.Errorf("expected to get value \"db_B\" but got %s", value) + } + for _, name := range names { if name == mf.GetName() { results[name] = result{found: true}