-
-
Notifications
You must be signed in to change notification settings - Fork 9
/
stats.go
150 lines (125 loc) · 4.45 KB
/
stats.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
141
142
143
144
145
146
147
148
149
150
package otelsql
import (
"context"
"database/sql"
"sync"
"time"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/metric"
)
// defaultMinimumReadDBStatsInterval is the default minimum interval between calls to db.Stats().
const defaultMinimumReadDBStatsInterval = time.Second
const (
dbSQLConnectionsOpen = "db.sql.connections.open"
dbSQLConnectionsIdle = "db.sql.connections.idle"
dbSQLConnectionsActive = "db.sql.connections.active"
dbSQLConnectionsWaitCount = "db.sql.connections.wait_count"
dbSQLConnectionsWaitDuration = "db.sql.connections.wait_duration"
dbSQLConnectionsIdleClosed = "db.sql.connections.idle_closed"
dbSQLConnectionsLifetimeClosed = "db.sql.connections.lifetime_closed"
)
// RecordStats records database statistics for provided sql.DB at the provided interval.
func RecordStats(db *sql.DB, opts ...StatsOption) error {
o := statsOptions{
meterProvider: otel.GetMeterProvider(),
minimumReadDBStatsInterval: defaultMinimumReadDBStatsInterval,
}
for _, opt := range opts {
opt.applyStatsOptions(&o)
}
meter := o.meterProvider.Meter(instrumentationName)
return recordStats(meter, db, o.minimumReadDBStatsInterval, o.defaultAttributes...)
}
// nolint: funlen
func recordStats(
meter metric.Meter,
db *sql.DB,
minimumReadDBStatsInterval time.Duration,
attrs ...attribute.KeyValue,
) error {
var (
err error
openConnections metric.Int64ObservableGauge
idleConnections metric.Int64ObservableGauge
activeConnections metric.Int64ObservableGauge
waitCount metric.Int64ObservableGauge
waitDuration metric.Float64ObservableGauge
idleClosed metric.Int64ObservableGauge
lifetimeClosed metric.Int64ObservableGauge
dbStats sql.DBStats
lastDBStats time.Time
// lock prevents a race between batch observer and instrument registration.
lock sync.Mutex
)
lock.Lock()
defer lock.Unlock()
openConnections, err = meter.Int64ObservableGauge(
dbSQLConnectionsOpen,
metric.WithUnit(unitDimensionless),
metric.WithDescription("Count of open connections in the pool"),
)
handleErr(err)
idleConnections, err = meter.Int64ObservableGauge(
dbSQLConnectionsIdle,
metric.WithUnit(unitDimensionless),
metric.WithDescription("Count of idle connections in the pool"),
)
handleErr(err)
activeConnections, err = meter.Int64ObservableGauge(
dbSQLConnectionsActive,
metric.WithUnit(unitDimensionless),
metric.WithDescription("Count of active connections in the pool"),
)
handleErr(err)
waitCount, err = meter.Int64ObservableGauge(
dbSQLConnectionsWaitCount,
metric.WithUnit(unitDimensionless),
metric.WithDescription("The total number of connections waited for"),
)
handleErr(err)
waitDuration, err = meter.Float64ObservableGauge(
dbSQLConnectionsWaitDuration,
metric.WithUnit(unitMilliseconds),
metric.WithDescription("The total time blocked waiting for a new connection"),
)
handleErr(err)
idleClosed, err = meter.Int64ObservableGauge(
dbSQLConnectionsIdleClosed,
metric.WithUnit(unitDimensionless),
metric.WithDescription("The total number of connections closed due to SetMaxIdleConns"),
)
handleErr(err)
lifetimeClosed, err = meter.Int64ObservableGauge(
dbSQLConnectionsLifetimeClosed,
metric.WithUnit(unitDimensionless),
metric.WithDescription("The total number of connections closed due to SetConnMaxLifetime"),
)
handleErr(err)
_, err = meter.RegisterCallback(func(_ context.Context, obs metric.Observer) error {
lock.Lock()
defer lock.Unlock()
now := time.Now()
if now.Sub(lastDBStats) >= minimumReadDBStatsInterval {
dbStats = db.Stats()
lastDBStats = now
}
obs.ObserveInt64(openConnections, int64(dbStats.OpenConnections), metric.WithAttributes(attrs...))
obs.ObserveInt64(idleConnections, int64(dbStats.Idle), metric.WithAttributes(attrs...))
obs.ObserveInt64(activeConnections, int64(dbStats.InUse), metric.WithAttributes(attrs...))
obs.ObserveInt64(waitCount, dbStats.WaitCount, metric.WithAttributes(attrs...))
obs.ObserveFloat64(waitDuration, float64(dbStats.WaitDuration.Nanoseconds())/1e6, metric.WithAttributes(attrs...))
obs.ObserveInt64(idleClosed, dbStats.MaxIdleClosed, metric.WithAttributes(attrs...))
obs.ObserveInt64(lifetimeClosed, dbStats.MaxLifetimeClosed, metric.WithAttributes(attrs...))
return nil
},
openConnections,
idleConnections,
activeConnections,
waitCount,
waitDuration,
idleClosed,
lifetimeClosed,
)
return err
}