-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathexporter.go
129 lines (112 loc) · 3.26 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
package exporter
import (
"context"
"fmt"
"sync"
"time"
"github.com/odonate/postgres-exporter/exporter/collectors"
"github.com/odonate/postgres-exporter/exporter/db"
"github.com/odonate/postgres-exporter/exporter/logging"
"github.com/prometheus/client_golang/prometheus"
"golang.org/x/sync/errgroup"
)
var log = logging.NewLogger()
const namespace = "pg_stat"
// Opts for the exporter.
type Opts struct {
DBOpts []db.Opts
}
// Exporter collects PostgreSQL metrics and exports them via prometheus.
type Exporter struct {
dbClients []*db.Client
collectors []collectors.Collector
// Internal metrics.
up prometheus.Gauge
totalScrapes prometheus.Counter
mutex sync.RWMutex
}
// MustNew instantiates and returns a new Exporter or panics.
func MustNew(ctx context.Context, opts Opts) *Exporter {
exporter, err := New(ctx, opts)
if err != nil {
panic(err)
}
return exporter
}
// New instaniates and returns a new Exporter.
func New(ctx context.Context, opts Opts) (*Exporter, error) {
if len(opts.DBOpts) < 1 {
return nil, fmt.Errorf("missing db opts")
}
dbClients := make([]*db.Client, 0, len(opts.DBOpts))
for _, dbOpt := range opts.DBOpts {
dbClient, err := db.New(ctx, dbOpt)
if err != nil {
return nil, fmt.Errorf("creating exporter: %w", err)
}
dbClients = append(dbClients, dbClient)
}
return &Exporter{
dbClients: dbClients,
collectors: collectors.DefaultCollectors(dbClients),
// Internal metrics.
up: prometheus.NewGauge(prometheus.GaugeOpts{
Namespace: namespace,
Name: "up",
Help: "Was the last scrape of PostgreSQL successful.",
}),
totalScrapes: prometheus.NewCounter(prometheus.CounterOpts{
Namespace: namespace,
Name: "exporter_scrapes_total",
Help: "Current total PostgreSQL scrapes",
}),
}, nil
}
// WithCustomCollectors lets the exporter scrape custom metrics.
func (e *Exporter) WithCustomCollectors(collectors ...collectors.Collector) *Exporter {
e.collectors = append(e.collectors, collectors...)
return e
}
// Register the exporter.
func (e *Exporter) Register() { prometheus.MustRegister(e) }
// HealthCheck pings PostgreSQL.
func (e *Exporter) HealthCheck(ctx context.Context) error {
group := errgroup.Group{}
for _, dbClient := range e.dbClients {
ctx := ctx
dbClient := dbClient
group.Go(func() error { return dbClient.CheckConnection(ctx) })
}
return group.Wait()
}
// Describe implements the prometheus.Collector.
func (e *Exporter) Describe(ch chan<- *prometheus.Desc) {
// Internal metrics.
ch <- e.up.Desc()
ch <- e.totalScrapes.Desc()
for _, collector := range e.collectors {
collector.Describe(ch)
}
}
// Collect implements the promtheus.Collector.
func (e *Exporter) Collect(ch chan<- prometheus.Metric) {
start := time.Now()
defer func() {
log.Infof("exporter collect took %dms", time.Now().Sub(start).Milliseconds())
}()
e.mutex.Lock()
defer e.mutex.Unlock()
e.totalScrapes.Inc()
up := 1
group := errgroup.Group{}
for _, collector := range e.collectors {
collector := collector
group.Go(func() error { return collector.Scrape(ch) })
}
if err := group.Wait(); err != nil {
up = 0
fmt.Println(fmt.Sprintf("collecting: %w", err))
}
ch <- prometheus.MustNewConstMetric(e.up.Desc(), prometheus.GaugeValue, float64(up))
ch <- e.totalScrapes
}