-
-
Notifications
You must be signed in to change notification settings - Fork 21
/
prometheus.go
147 lines (119 loc) · 3.43 KB
/
prometheus.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
package prometheus
import (
"context"
"fmt"
"net/http"
"sync"
"time"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"
"github.com/prometheus/client_golang/prometheus/push"
"gorm.io/gorm"
)
var (
_ gorm.Plugin = &Prometheus{}
)
const (
defaultRefreshInterval = 15 // the prometheus default pull metrics every 15 seconds
defaultHTTPServerPort = 8080 // default pull port
)
type MetricsCollector interface {
Metrics(*Prometheus) []prometheus.Collector
}
type Prometheus struct {
*gorm.DB
*DBStats
*Config
refreshOnce, pushOnce sync.Once
Labels map[string]string
Collectors []prometheus.Collector
}
type Config struct {
DBName string // use DBName as metrics label
RefreshInterval uint32 // refresh metrics interval.
PushAddr string // prometheus pusher address
PushUser string // prometheus pusher basic auth user
PushPassword string // prometheus pusher basic auth password
StartServer bool // if true, create http server to expose metrics
HTTPServerPort uint32 // http server port
MetricsCollector []MetricsCollector // collector
Labels map[string]string // metrics labels
}
func New(config Config) *Prometheus {
if config.RefreshInterval == 0 {
config.RefreshInterval = defaultRefreshInterval
}
if config.HTTPServerPort == 0 {
config.HTTPServerPort = defaultHTTPServerPort
}
labels := make(map[string]string)
if config.Labels != nil {
labels = config.Labels
}
return &Prometheus{Config: &config, Labels: labels}
}
func (p *Prometheus) Name() string {
return "gorm:prometheus"
}
func (p *Prometheus) Initialize(db *gorm.DB) error { // can be called repeatedly
p.DB = db
if p.Config.DBName != "" {
p.Labels["db_name"] = p.Config.DBName
}
p.DBStats = newStats(p.Labels)
p.refreshOnce.Do(func() {
for _, mc := range p.MetricsCollector {
p.Collectors = append(p.Collectors, mc.Metrics(p)...)
}
go func() {
for range time.Tick(time.Duration(p.Config.RefreshInterval) * time.Second) {
p.refresh()
}
}()
})
if p.Config.StartServer {
httpServerOnce.Do(func() { // only start once
go p.startServer()
})
}
if p.PushAddr != "" {
p.pushOnce.Do(func() {
go p.startPush()
})
}
return nil
}
func (p *Prometheus) refresh() {
if db, err := p.DB.DB(); err == nil {
p.DBStats.Set(db.Stats())
} else {
p.DB.Logger.Error(context.Background(), "gorm:prometheus failed to collect db status, got error: %v", err)
}
}
func (p *Prometheus) startPush() {
pusher := push.New(p.PushAddr, p.DBName)
if p.PushUser != "" || p.PushPassword != "" {
pusher.BasicAuth(p.PushUser, p.PushPassword)
}
for _, collector := range p.DBStats.Collectors() {
pusher = pusher.Collector(collector)
}
for _, c := range p.Collectors {
pusher = pusher.Collector(c)
}
for range time.Tick(time.Duration(p.Config.RefreshInterval) * time.Second) {
err := pusher.Push()
if err != nil {
p.DB.Logger.Error(context.Background(), "gorm:prometheus push err: ", err)
}
}
}
var httpServerOnce sync.Once
func (p *Prometheus) startServer() {
mux := http.NewServeMux()
mux.Handle("/metrics", promhttp.Handler())
err := http.ListenAndServe(fmt.Sprintf(":%d", p.Config.HTTPServerPort), mux)
if err != nil {
p.DB.Logger.Error(context.Background(), "gorm:prometheus listen and serve err: ", err)
}
}