-
Notifications
You must be signed in to change notification settings - Fork 16
/
exporter.go
140 lines (124 loc) · 4.63 KB
/
exporter.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
package dbstats
import (
"database/sql"
"errors"
"time"
"github.com/RussellLuo/kun/pkg/prometheus/metric"
"github.com/RussellLuo/kun/pkg/tickdoer"
"github.com/prometheus/client_golang/prometheus"
)
var (
ErrBadDB = errors.New("db is nil")
ErrDBExists = errors.New("db already exists")
)
type Opts struct {
Namespace string
Subsystem string
UpdateInterval time.Duration // How often we fetch and update the DBStats.
}
// Metrics for sql.DBStats.
type Metrics struct {
MaxOpenConnections *prometheus.GaugeVec // Maximum number of open connections to the database.
// Pool Status
OpenConnections *prometheus.GaugeVec // The number of established connections both in use and idle.
InUse *prometheus.GaugeVec // The number of connections currently in use.
Idle *prometheus.GaugeVec // The number of idle connections.
// Counters
WaitCount *metric.SettableCounterVec // The total number of connections waited for.
WaitDuration *metric.SettableCounterVec // The total time (in seconds) blocked waiting for a new connection.
MaxIdleClosed *metric.SettableCounterVec // The total number of connections closed due to SetMaxIdleConns.
MaxLifetimeClosed *metric.SettableCounterVec // The total number of connections closed due to SetConnMaxLifetime.
}
// Exporter exports sql.DBStats as Prometheus metrics.
type Exporter struct {
opts *Opts
metrics *Metrics
dbLabels map[*sql.DB]prometheus.Labels
doer *tickdoer.TickDoer
}
func NewExporter(opts *Opts, labelNames []string) *Exporter {
return &Exporter{
opts: opts,
metrics: &Metrics{
MaxOpenConnections: metric.NewGaugeVecFrom(prometheus.GaugeOpts{
Namespace: opts.Namespace,
Subsystem: opts.Subsystem,
Name: "max_open_connections",
Help: "Maximum number of open connections to the database.",
}, labelNames),
OpenConnections: metric.NewGaugeVecFrom(prometheus.GaugeOpts{
Namespace: opts.Namespace,
Subsystem: opts.Subsystem,
Name: "open_connections",
Help: "The number of established connections both in use and idle.",
}, labelNames),
InUse: metric.NewGaugeVecFrom(prometheus.GaugeOpts{
Namespace: opts.Namespace,
Subsystem: opts.Subsystem,
Name: "in_use",
Help: "The number of connections currently in use.",
}, labelNames),
Idle: metric.NewGaugeVecFrom(prometheus.GaugeOpts{
Namespace: opts.Namespace,
Subsystem: opts.Subsystem,
Name: "idle",
Help: "The number of idle connections.",
}, labelNames),
WaitCount: metric.NewSettableCounterVecFrom(metric.SettableCounterOpts{
Namespace: opts.Namespace,
Subsystem: opts.Subsystem,
Name: "wait_count",
Help: "The total number of connections waited for.",
}, labelNames),
WaitDuration: metric.NewSettableCounterVecFrom(metric.SettableCounterOpts{
Namespace: opts.Namespace,
Subsystem: opts.Subsystem,
Name: "wait_duration",
Help: "The total time (in seconds) blocked waiting for a new connection.",
}, labelNames),
MaxIdleClosed: metric.NewSettableCounterVecFrom(metric.SettableCounterOpts{
Namespace: opts.Namespace,
Subsystem: opts.Subsystem,
Name: "max_idle_closed",
Help: "The total number of connections closed due to SetMaxIdleConns.",
}, labelNames),
MaxLifetimeClosed: metric.NewSettableCounterVecFrom(metric.SettableCounterOpts{
Namespace: opts.Namespace,
Subsystem: opts.Subsystem,
Name: "max_lifetime_closed",
Help: "The total number of connections closed due to SetConnMaxLifetime.",
}, labelNames),
},
dbLabels: make(map[*sql.DB]prometheus.Labels),
}
}
func (e *Exporter) MustBind(db *sql.DB, labelValues ...string) {
if db == nil {
panic(ErrBadDB)
}
if _, ok := e.dbLabels[db]; ok {
panic(ErrDBExists)
}
e.dbLabels[db] = metric.MakeLabels(labelValues...)
}
func (e *Exporter) update() {
for db, labels := range e.dbLabels {
stats := db.Stats()
e.metrics.MaxOpenConnections.With(labels).Set(float64(stats.MaxOpenConnections))
e.metrics.OpenConnections.With(labels).Set(float64(stats.OpenConnections))
e.metrics.InUse.With(labels).Set(float64(stats.InUse))
e.metrics.Idle.With(labels).Set(float64(stats.Idle))
e.metrics.WaitCount.With(labels).Set(float64(stats.WaitCount))
e.metrics.WaitDuration.With(labels).Set(stats.WaitDuration.Seconds())
e.metrics.MaxIdleClosed.With(labels).Set(float64(stats.MaxIdleClosed))
e.metrics.MaxLifetimeClosed.With(labels).Set(float64(stats.MaxLifetimeClosed))
}
}
func (e *Exporter) Start() {
if e.doer == nil {
e.doer = tickdoer.TickFunc(e.opts.UpdateInterval, e.update)
}
}
func (e *Exporter) Stop() {
e.doer.Stop()
}