Skip to content

Commit 713461d

Browse files
committed
WIP: Add prelim multi-target support
- Remove multi server support from new collector package - Add http handler for multi-target support Signed-off-by: Joe Adams <[email protected]>
1 parent 58cc383 commit 713461d

File tree

8 files changed

+238
-170
lines changed

8 files changed

+238
-170
lines changed

cmd/postgres_exporter/datasource.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,12 @@ func getDataSources() ([]string, error) {
162162
uri = os.Getenv("DATA_SOURCE_URI")
163163
}
164164

165+
// No datasources found. This allows us to support the multi-target pattern
166+
// withouth an explicit datasource.
167+
if uri == "" {
168+
return []string{}, nil
169+
}
170+
165171
dsn = "postgresql://" + ui + "@" + uri
166172

167173
return []string{dsn}, nil

cmd/postgres_exporter/main.go

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -85,16 +85,17 @@ func main() {
8585
return
8686
}
8787

88-
dsn, err := getDataSources()
88+
dsns, err := getDataSources()
8989
if err != nil {
9090
level.Error(logger).Log("msg", "Failed reading data sources", "err", err.Error())
9191
os.Exit(1)
9292
}
9393

94-
if len(dsn) == 0 {
95-
level.Error(logger).Log("msg", "Couldn't find environment variables describing the datasource to use")
96-
os.Exit(1)
97-
}
94+
// TODO(@sysadmind): Remove this with multi-target support
95+
// if len(dsn) == 0 {
96+
// level.Error(logger).Log("msg", "Couldn't find environment variables describing the datasource to use")
97+
// os.Exit(1)
98+
// }
9899

99100
opts := []ExporterOpt{
100101
DisableDefaultMetrics(*disableDefaultMetrics),
@@ -106,7 +107,7 @@ func main() {
106107
IncludeDatabases(*includeDatabases),
107108
}
108109

109-
exporter := NewExporter(dsn, opts...)
110+
exporter := NewExporter(dsns, opts...)
110111
defer func() {
111112
exporter.servers.Close()
112113
}()
@@ -115,23 +116,31 @@ func main() {
115116

116117
prometheus.MustRegister(exporter)
117118

119+
// TODO(@sysadmind): Remove this with multi-target support. We are removing multiple DSN support
120+
dsn := ""
121+
if len(dsns) > 0 {
122+
dsn = dsns[0]
123+
}
124+
118125
pe, err := collector.NewPostgresCollector(
119126
logger,
120127
dsn,
121128
[]string{},
122129
)
123130
if err != nil {
124131
level.Error(logger).Log("msg", "Failed to create PostgresCollector", "err", err.Error())
125-
os.Exit(1)
132+
} else {
133+
prometheus.MustRegister(pe)
126134
}
127-
prometheus.MustRegister(pe)
128135

129136
http.Handle(*metricPath, promhttp.Handler())
130137
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
131138
w.Header().Set("Content-Type", "text/html; charset=UTF-8") // nolint: errcheck
132139
w.Write(landingPage) // nolint: errcheck
133140
})
134141

142+
http.HandleFunc("/probe", handleProbe(logger))
143+
135144
level.Info(logger).Log("msg", "Listening on address", "address", *listenAddress)
136145
srv := &http.Server{Addr: *listenAddress}
137146
if err := web.ListenAndServe(srv, *webConfig, logger); err != nil {

cmd/postgres_exporter/probe.go

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
// Copyright 2022 The Prometheus Authors
2+
// Licensed under the Apache License, Version 2.0 (the "License");
3+
// you may not use this file except in compliance with the License.
4+
// You may obtain a copy of the License at
5+
//
6+
// http://www.apache.org/licenses/LICENSE-2.0
7+
//
8+
// Unless required by applicable law or agreed to in writing, software
9+
// distributed under the License is distributed on an "AS IS" BASIS,
10+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
// See the License for the specific language governing permissions and
12+
// limitations under the License.
13+
14+
package main
15+
16+
import (
17+
"net/http"
18+
"time"
19+
20+
"github.com/go-kit/log"
21+
"github.com/prometheus-community/postgres_exporter/collector"
22+
"github.com/prometheus/client_golang/prometheus"
23+
"github.com/prometheus/client_golang/prometheus/promhttp"
24+
)
25+
26+
func handleProbe(logger log.Logger) http.HandlerFunc {
27+
return func(w http.ResponseWriter, r *http.Request) {
28+
ctx := r.Context()
29+
params := r.URL.Query()
30+
target := params.Get("target")
31+
if target == "" {
32+
http.Error(w, "target is required", http.StatusBadRequest)
33+
return
34+
}
35+
36+
// TODO: Timeout
37+
// TODO: Auth Module
38+
39+
probeSuccessGauge := prometheus.NewGauge(prometheus.GaugeOpts{
40+
Name: "probe_success",
41+
Help: "Displays whether or not the probe was a success",
42+
})
43+
probeDurationGauge := prometheus.NewGauge(prometheus.GaugeOpts{
44+
Name: "probe_duration_seconds",
45+
Help: "Returns how long the probe took to complete in seconds",
46+
})
47+
48+
tl := log.With(logger, "target", target)
49+
_ = tl
50+
51+
start := time.Now()
52+
registry := prometheus.NewRegistry()
53+
registry.MustRegister(probeSuccessGauge)
54+
registry.MustRegister(probeDurationGauge)
55+
56+
// TODO(@sysadmind): this is a temp hack until we have a proper auth module
57+
target = "postgres://postgres:test@localhost:5432/circle_test?sslmode=disable"
58+
59+
// Run the probe
60+
pc, err := collector.NewProbeCollector(tl, registry, target)
61+
if err != nil {
62+
probeSuccessGauge.Set(0)
63+
probeDurationGauge.Set(time.Since(start).Seconds())
64+
http.Error(w, err.Error(), http.StatusInternalServerError)
65+
return
66+
}
67+
_ = ctx
68+
69+
// TODO: Which way should this be? Register or handle the collection manually?
70+
// Also, what about the context?
71+
72+
// Option 1: Register the collector
73+
registry.MustRegister(pc)
74+
75+
// Option 2: Handle the collection manually. This allows us to collect duration metrics.
76+
// The collectors themselves already support their own duration metrics.
77+
// err = pc.Update(ctx)
78+
// if err != nil {
79+
// probeSuccessGauge.Set(0)
80+
// } else {
81+
// probeSuccessGauge.Set(1)
82+
// }
83+
84+
duration := time.Since(start).Seconds()
85+
probeDurationGauge.Set(duration)
86+
87+
// TODO check success, etc
88+
h := promhttp.HandlerFor(registry, promhttp.HandlerOpts{})
89+
h.ServeHTTP(w, r)
90+
}
91+
}

collector/collector.go

Lines changed: 16 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ package collector
1515

1616
import (
1717
"context"
18+
"database/sql"
1819
"errors"
1920
"fmt"
2021
"sync"
@@ -58,7 +59,7 @@ var (
5859
)
5960

6061
type Collector interface {
61-
Update(ctx context.Context, server *server, ch chan<- prometheus.Metric) error
62+
Update(ctx context.Context, db *sql.DB, ch chan<- prometheus.Metric) error
6263
}
6364

6465
func registerCollector(name string, isDefaultEnabled bool, createFunc func(logger log.Logger) (Collector, error)) {
@@ -86,13 +87,13 @@ type PostgresCollector struct {
8687
Collectors map[string]Collector
8788
logger log.Logger
8889

89-
servers map[string]*server
90+
db *sql.DB
9091
}
9192

9293
type Option func(*PostgresCollector) error
9394

9495
// NewPostgresCollector creates a new PostgresCollector.
95-
func NewPostgresCollector(logger log.Logger, dsns []string, filters []string, options ...Option) (*PostgresCollector, error) {
96+
func NewPostgresCollector(logger log.Logger, dsn string, filters []string, options ...Option) (*PostgresCollector, error) {
9697
p := &PostgresCollector{
9798
logger: logger,
9899
}
@@ -136,17 +137,18 @@ func NewPostgresCollector(logger log.Logger, dsns []string, filters []string, op
136137

137138
p.Collectors = collectors
138139

139-
servers := make(map[string]*server)
140-
for _, dsn := range dsns {
141-
s, err := makeServer(dsn)
142-
if err != nil {
143-
return nil, err
144-
}
140+
if dsn == "" {
141+
return nil, errors.New("empty dsn")
142+
}
145143

146-
servers[dsn] = s
144+
db, err := sql.Open("postgres", dsn)
145+
if err != nil {
146+
return nil, err
147147
}
148+
db.SetMaxOpenConns(1)
149+
db.SetMaxIdleConns(1)
148150

149-
p.servers = servers
151+
p.db = db
150152

151153
return p, nil
152154
}
@@ -160,32 +162,20 @@ func (p PostgresCollector) Describe(ch chan<- *prometheus.Desc) {
160162
// Collect implements the prometheus.Collector interface.
161163
func (p PostgresCollector) Collect(ch chan<- prometheus.Metric) {
162164
ctx := context.TODO()
163-
wg := sync.WaitGroup{}
164-
wg.Add(len(p.servers))
165-
for _, s := range p.servers {
166-
go func(s *server) {
167-
p.subCollect(ctx, s, ch)
168-
wg.Done()
169-
}(s)
170-
}
171-
wg.Wait()
172-
}
173-
174-
func (p PostgresCollector) subCollect(ctx context.Context, server *server, ch chan<- prometheus.Metric) {
175165
wg := sync.WaitGroup{}
176166
wg.Add(len(p.Collectors))
177167
for name, c := range p.Collectors {
178168
go func(name string, c Collector) {
179-
execute(ctx, name, c, server, ch, p.logger)
169+
execute(ctx, name, c, p.db, ch, p.logger)
180170
wg.Done()
181171
}(name, c)
182172
}
183173
wg.Wait()
184174
}
185175

186-
func execute(ctx context.Context, name string, c Collector, s *server, ch chan<- prometheus.Metric, logger log.Logger) {
176+
func execute(ctx context.Context, name string, c Collector, db *sql.DB, ch chan<- prometheus.Metric, logger log.Logger) {
187177
begin := time.Now()
188-
err := c.Update(ctx, s, ch)
178+
err := c.Update(ctx, db, ch)
189179
duration := time.Since(begin)
190180
var success float64
191181

collector/pg_database.go

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ package collector
1515

1616
import (
1717
"context"
18+
"database/sql"
1819

1920
"github.com/go-kit/log"
2021
"github.com/prometheus/client_golang/prometheus"
@@ -36,15 +37,11 @@ var pgDatabase = map[string]*prometheus.Desc{
3637
"size_bytes": prometheus.NewDesc(
3738
"pg_database_size_bytes",
3839
"Disk space used by the database",
39-
[]string{"datname", "server"}, nil,
40+
[]string{"datname"}, nil,
4041
),
4142
}
4243

43-
func (PGDatabaseCollector) Update(ctx context.Context, server *server, ch chan<- prometheus.Metric) error {
44-
db, err := server.GetDB()
45-
if err != nil {
46-
return err
47-
}
44+
func (PGDatabaseCollector) Update(ctx context.Context, db *sql.DB, ch chan<- prometheus.Metric) error {
4845
rows, err := db.QueryContext(ctx,
4946
`SELECT pg_database.datname
5047
,pg_database_size(pg_database.datname)
@@ -63,7 +60,7 @@ func (PGDatabaseCollector) Update(ctx context.Context, server *server, ch chan<-
6360

6461
ch <- prometheus.MustNewConstMetric(
6562
pgDatabase["size_bytes"],
66-
prometheus.GaugeValue, float64(size), datname, server.GetName(),
63+
prometheus.GaugeValue, float64(size), datname,
6764
)
6865
}
6966
if err := rows.Err(); err != nil {

0 commit comments

Comments
 (0)