Skip to content

Commit 0b09fbb

Browse files
Merge pull request #13 from form3tech-oss/ap-stmnt-summary-collector
feat: add custom collector for statement summary
2 parents cd450cd + fec78ce commit 0b09fbb

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+1326
-868
lines changed

.github/workflows/calculate-tag.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ jobs:
3434
if [[ "${{ github.ref_type }}" = "tag" ]]; then
3535
latest_tag="${GITHUB_REF#refs/tags/}"
3636
else
37-
latest_tag=$(git describe --tags --abbrev=0)
37+
latest_tag=$(git describe --tags --abbrev=1)
3838
fi
3939
echo "latest_tag=$latest_tag" >> $GITHUB_OUTPUT
4040
- name: Calculate pre-release suffix

.github/workflows/ci.yaml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,10 @@ jobs:
3838
- name: Package
3939
run: make crossbuild-tarballs
4040
- name: Archive artifacts
41-
uses: actions/upload-artifact@v3
41+
uses: actions/upload-artifact@v4
4242
with:
4343
name: tarballs
44+
include-hidden-files: true
4445
path: ./.tarballs/*.tar.gz
4546

4647
commit-message:
@@ -67,7 +68,7 @@ jobs:
6768
if: contains(needs.commit-message.outputs.head-commit-message, 'pre-release') || startsWith(github.ref, 'refs/tags')
6869
steps:
6970
- name: Download artifacts
70-
uses: actions/download-artifact@v3
71+
uses: actions/download-artifact@v4
7172
with:
7273
name: tarballs
7374
- name: Create GH release

.promu.yml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
go:
2-
# This must match .circle/config.yml.
3-
version: 1.21
2+
version: 1.22
43
repository:
54
path: github.com/form3tech-oss/postgres_exporter
65
build:

VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
0.15.0
1+
0.15.3

cmd/postgres_exporter/main.go

Lines changed: 56 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@
1414
package main
1515

1616
import (
17+
"errors"
1718
"fmt"
19+
"net"
1820
"net/http"
1921
"os"
2022
"strings"
@@ -46,6 +48,7 @@ var (
4648
autoDiscoverDatabases = kingpin.Flag("auto-discover-databases", "Whether to discover the databases on a server dynamically. (DEPRECATED)").Default("false").Envar("PG_EXPORTER_AUTO_DISCOVER_DATABASES").Bool()
4749
queriesPath = kingpin.Flag("extend.query-path", "Path to custom queries to run. (DEPRECATED)").Default("").Envar("PG_EXPORTER_EXTEND_QUERY_PATH").String()
4850
onlyDumpMaps = kingpin.Flag("dumpmaps", "Do not run, simply dump the maps.").Bool()
51+
onlyHealthCheck = kingpin.Flag("healthcheck", "Do not run, just return if up and running.").Bool()
4952
constantLabelsList = kingpin.Flag("constantLabels", "A list of label=value separated by comma(,). (DEPRECATED)").Default("").Envar("PG_EXPORTER_CONSTANT_LABELS").String()
5053
excludeDatabases = kingpin.Flag("exclude-databases", "A list of databases to remove when autoDiscoverDatabases is enabled (DEPRECATED)").Default("").Envar("PG_EXPORTER_EXCLUDE_DATABASES").String()
5154
includeDatabases = kingpin.Flag("include-databases", "A list of databases to include when autoDiscoverDatabases is enabled (DEPRECATED)").Default("").Envar("PG_EXPORTER_INCLUDE_DATABASES").String()
@@ -81,6 +84,18 @@ func main() {
8184
return
8285
}
8386

87+
if *onlyHealthCheck {
88+
healthy, err := runHealthCheck(webConfig)
89+
if err != nil {
90+
level.Error(logger).Log("msg", "error running health check", "err", err)
91+
}
92+
if healthy {
93+
level.Info(logger).Log("msg", "health ok")
94+
os.Exit(0)
95+
}
96+
os.Exit(1)
97+
}
98+
8499
if err := c.ReloadConfig(*configFile, logger); err != nil {
85100
// This is not fatal, but it means that auth must be provided for every dsn.
86101
level.Warn(logger).Log("msg", "Error loading config", "err", err)
@@ -122,29 +137,38 @@ func main() {
122137
exporter.servers.Close()
123138
}()
124139

125-
prometheus.MustRegister(version.NewCollector(exporterName))
140+
reg := prometheus.NewRegistry()
126141

127-
prometheus.MustRegister(exporter)
142+
reg.MustRegister(
143+
version.NewCollector(exporterName),
144+
exporter,
145+
)
128146

129147
// TODO(@sysadmind): Remove this with multi-target support. We are removing multiple DSN support
130148
dsn := ""
131149
if len(dsns) > 0 {
132150
dsn = dsns[0]
133151
}
152+
153+
collOpts := []collector.Option{
154+
collector.WithConstantLabels(parseConstLabels(*constantLabelsList)),
155+
}
134156

135-
pe, err := collector.NewPostgresCollector(
157+
pgColl, err := collector.NewPostgresCollector(
136158
logger,
137159
excludedDatabases,
138160
dsn,
139161
[]string{},
162+
collOpts...
140163
)
141164
if err != nil {
142165
level.Warn(logger).Log("msg", "Failed to create PostgresCollector", "err", err.Error())
143166
} else {
144-
prometheus.MustRegister(pe)
167+
168+
reg.MustRegister(pgColl)
145169
}
146170

147-
http.Handle(*metricsPath, promhttp.Handler())
171+
http.Handle(*metricsPath, promhttp.HandlerFor(reg, promhttp.HandlerOpts{}))
148172

149173
if *metricsPath != "/" && *metricsPath != "" {
150174
landingConfig := web.LandingConfig{
@@ -174,3 +198,30 @@ func main() {
174198
os.Exit(1)
175199
}
176200
}
201+
202+
func runHealthCheck(webConfig *web.FlagConfig) (bool, error) {
203+
if len(*webConfig.WebListenAddresses) == 0 {
204+
return false, errors.New("no listen addresses to run the request to")
205+
}
206+
addr := (*webConfig.WebListenAddresses)[0]
207+
host, port, err := net.SplitHostPort(addr)
208+
if err != nil {
209+
return false, err
210+
}
211+
if host == "" {
212+
host = "localhost"
213+
}
214+
url := fmt.Sprintf("http://%s:%s/", host, port)
215+
req, err := http.NewRequest("GET", url, nil)
216+
if err != nil {
217+
return false, err
218+
}
219+
220+
resp, err := http.DefaultClient.Do(req)
221+
if err != nil {
222+
return false, err
223+
}
224+
225+
defer resp.Body.Close()
226+
return resp.StatusCode == 200, nil
227+
}

cmd/postgres_exporter/postgres_exporter.go

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -415,7 +415,7 @@ type Exporter struct {
415415
userQueriesPath string
416416
constantLabels prometheus.Labels
417417
duration prometheus.Gauge
418-
error prometheus.Gauge
418+
errors prometheus.Gauge
419419
psqlUp prometheus.Gauge
420420
userQueriesError *prometheus.GaugeVec
421421
totalScrapes prometheus.Counter
@@ -537,7 +537,7 @@ func (e *Exporter) setupInternalMetrics() {
537537
Help: "Total number of times PostgreSQL was scraped for metrics.",
538538
ConstLabels: e.constantLabels,
539539
})
540-
e.error = prometheus.NewGauge(prometheus.GaugeOpts{
540+
e.errors = prometheus.NewGauge(prometheus.GaugeOpts{
541541
Namespace: namespace,
542542
Subsystem: exporter,
543543
Name: "last_scrape_error",
@@ -569,7 +569,7 @@ func (e *Exporter) Collect(ch chan<- prometheus.Metric) {
569569

570570
ch <- e.duration
571571
ch <- e.totalScrapes
572-
ch <- e.error
572+
ch <- e.errors
573573
ch <- e.psqlUp
574574
e.userQueriesError.Collect(ch)
575575
}
@@ -676,6 +676,7 @@ func (e *Exporter) scrape(ch chan<- prometheus.Metric) {
676676
var connectionErrorsCount int
677677

678678
for _, dsn := range dsns {
679+
level.Debug(logger).Log("msg", "start scrape", "dsn", dsn)
679680
if err := e.scrapeDSN(ch, dsn); err != nil {
680681
errorsCount++
681682

@@ -696,8 +697,8 @@ func (e *Exporter) scrape(ch chan<- prometheus.Metric) {
696697

697698
switch errorsCount {
698699
case 0:
699-
e.error.Set(0)
700+
e.errors.Set(0)
700701
default:
701-
e.error.Set(1)
702+
e.errors.Set(1)
702703
}
703704
}

collector/collector.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ type Collector interface {
6464
type collectorConfig struct {
6565
logger log.Logger
6666
excludeDatabases []string
67+
constantLabels prometheus.Labels
6768
}
6869

6970
func registerCollector(name string, isDefaultEnabled bool, createFunc func(collectorConfig) (Collector, error)) {
@@ -92,10 +93,19 @@ type PostgresCollector struct {
9293
logger log.Logger
9394

9495
instance *instance
96+
constantLabels prometheus.Labels
9597
}
9698

9799
type Option func(*PostgresCollector) error
98100

101+
// WithConstantLabels configures constant labels.
102+
func WithConstantLabels(l prometheus.Labels) Option {
103+
return func(c *PostgresCollector) error {
104+
c.constantLabels = l
105+
return nil
106+
}
107+
}
108+
99109
// NewPostgresCollector creates a new PostgresCollector.
100110
func NewPostgresCollector(logger log.Logger, excludeDatabases []string, dsn string, filters []string, options ...Option) (*PostgresCollector, error) {
101111
p := &PostgresCollector{
@@ -134,6 +144,7 @@ func NewPostgresCollector(logger log.Logger, excludeDatabases []string, dsn stri
134144
collector, err := factories[key](collectorConfig{
135145
logger: log.With(logger, "collector", key),
136146
excludeDatabases: excludeDatabases,
147+
constantLabels: p.constantLabels,
137148
})
138149
if err != nil {
139150
return nil, err

collector/pg_database.go

Lines changed: 22 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ func init() {
3030
type PGDatabaseCollector struct {
3131
log log.Logger
3232
excludedDatabases []string
33+
pgDatabaseSizeDesc *prometheus.Desc
34+
pgDatabaseConnectionLimitsDesc *prometheus.Desc
3335
}
3436

3537
func NewPGDatabaseCollector(config collectorConfig) (Collector, error) {
@@ -40,29 +42,28 @@ func NewPGDatabaseCollector(config collectorConfig) (Collector, error) {
4042
return &PGDatabaseCollector{
4143
log: config.logger,
4244
excludedDatabases: exclude,
45+
pgDatabaseSizeDesc: prometheus.NewDesc(
46+
prometheus.BuildFQName(
47+
namespace,
48+
databaseSubsystem,
49+
"size_bytes",
50+
),
51+
"Disk space used by the database",
52+
[]string{"datname"}, config.constantLabels,
53+
),
54+
pgDatabaseConnectionLimitsDesc: prometheus.NewDesc(
55+
prometheus.BuildFQName(
56+
namespace,
57+
databaseSubsystem,
58+
"connection_limit",
59+
),
60+
"Connection limit set for the database",
61+
[]string{"datname"}, config.constantLabels,
62+
),
4363
}, nil
4464
}
4565

4666
var (
47-
pgDatabaseSizeDesc = prometheus.NewDesc(
48-
prometheus.BuildFQName(
49-
namespace,
50-
databaseSubsystem,
51-
"size_bytes",
52-
),
53-
"Disk space used by the database",
54-
[]string{"datname"}, nil,
55-
)
56-
pgDatabaseConnectionLimitsDesc = prometheus.NewDesc(
57-
prometheus.BuildFQName(
58-
namespace,
59-
databaseSubsystem,
60-
"connection_limit",
61-
),
62-
"Connection limit set for the database",
63-
[]string{"datname"}, nil,
64-
)
65-
6667
pgDatabaseQuery = "SELECT pg_database.datname, pg_database.datconnlimit FROM pg_database;"
6768
pgDatabaseSizeQuery = "SELECT pg_database_size($1)"
6869
)
@@ -113,7 +114,7 @@ func (c PGDatabaseCollector) Update(ctx context.Context, instance *instance, ch
113114
connLimitMetric = float64(connLimit.Int64)
114115
}
115116
ch <- prometheus.MustNewConstMetric(
116-
pgDatabaseConnectionLimitsDesc,
117+
c.pgDatabaseConnectionLimitsDesc,
117118
prometheus.GaugeValue, connLimitMetric, database,
118119
)
119120
}
@@ -131,7 +132,7 @@ func (c PGDatabaseCollector) Update(ctx context.Context, instance *instance, ch
131132
sizeMetric = size.Float64
132133
}
133134
ch <- prometheus.MustNewConstMetric(
134-
pgDatabaseSizeDesc,
135+
c.pgDatabaseSizeDesc,
135136
prometheus.GaugeValue, sizeMetric, datname,
136137
)
137138

collector/pg_database_test.go

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import (
1717
"testing"
1818

1919
"github.com/DATA-DOG/go-sqlmock"
20+
"github.com/go-kit/log"
2021
"github.com/prometheus/client_golang/prometheus"
2122
dto "github.com/prometheus/client_model/go"
2223
"github.com/smartystreets/goconvey/convey"
@@ -40,7 +41,10 @@ func TestPGDatabaseCollector(t *testing.T) {
4041
ch := make(chan prometheus.Metric)
4142
go func() {
4243
defer close(ch)
43-
c := PGDatabaseCollector{}
44+
c, _ := NewPGDatabaseCollector(collectorConfig{
45+
logger: log.NewNopLogger(),
46+
constantLabels: prometheus.Labels{},
47+
})
4448
if err := c.Update(context.Background(), inst, ch); err != nil {
4549
t.Errorf("Error calling PGDatabaseCollector.Update: %s", err)
4650
}
@@ -81,7 +85,10 @@ func TestPGDatabaseCollectorNullMetric(t *testing.T) {
8185
ch := make(chan prometheus.Metric)
8286
go func() {
8387
defer close(ch)
84-
c := PGDatabaseCollector{}
88+
c, _ := NewPGDatabaseCollector(collectorConfig{
89+
logger: log.NewNopLogger(),
90+
constantLabels: prometheus.Labels{},
91+
})
8592
if err := c.Update(context.Background(), inst, ch); err != nil {
8693
t.Errorf("Error calling PGDatabaseCollector.Update: %s", err)
8794
}

0 commit comments

Comments
 (0)