Skip to content

Commit efce2f4

Browse files
authored
Merge branch 'prometheus-community:master' into master
2 parents b36d6b7 + 04bb60c commit efce2f4

Some content is hidden

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

43 files changed

+2808
-341
lines changed

.github/workflows/golangci-lint.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
---
2+
# This action is synced from https://github.com/prometheus/prometheus
13
name: golangci-lint
24
on:
35
push:
@@ -27,4 +29,4 @@ jobs:
2729
- name: Lint
2830
uses: golangci/[email protected]
2931
with:
30-
version: v1.51.2
32+
version: v1.53.3

.golangci.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
---
22
linters:
33
enable:
4+
- misspell
45
- revive
56

67
issues:
@@ -14,3 +15,9 @@ linters-settings:
1415
exclude-functions:
1516
# Never check for logger errors.
1617
- (github.com/go-kit/log.Logger).Log
18+
revive:
19+
rules:
20+
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#unused-parameter
21+
- name: unused-parameter
22+
severity: warning
23+
disabled: true

.yamllint

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,5 +20,4 @@ rules:
2020
config/testdata/section_key_dup.bad.yml
2121
line-length: disable
2222
truthy:
23-
ignore: |
24-
.github/workflows/*.yml
23+
check-keys: false

CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,13 @@
1+
## 0.13.2 / 2023-07-21
2+
3+
* [BUGFIX] Fix type issues on pg_postmaster metrics #828
4+
* [BUGFIX] Fix pg_replication collector instantiation #854
5+
* [BUGFIX] Fix pg_process_idle metrics #855
6+
7+
## 0.13.1 / 2023-06-27
8+
9+
* [BUGFIX] Make collectors not fail on null values #823
10+
111
## 0.13.0 / 2023-06-21
212

313
BREAKING CHANGES:

Makefile.common

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,13 +55,13 @@ ifneq ($(shell command -v gotestsum > /dev/null),)
5555
endif
5656
endif
5757

58-
PROMU_VERSION ?= 0.14.0
58+
PROMU_VERSION ?= 0.15.0
5959
PROMU_URL := https://github.com/prometheus/promu/releases/download/v$(PROMU_VERSION)/promu-$(PROMU_VERSION).$(GO_BUILD_PLATFORM).tar.gz
6060

6161
SKIP_GOLANGCI_LINT :=
6262
GOLANGCI_LINT :=
6363
GOLANGCI_LINT_OPTS ?=
64-
GOLANGCI_LINT_VERSION ?= v1.51.2
64+
GOLANGCI_LINT_VERSION ?= v1.53.3
6565
# golangci-lint only supports linux, darwin and windows platforms on i386/amd64.
6666
# windows isn't included here because of the path separator being different.
6767
ifeq ($(GOHOSTOS),$(filter $(GOHOSTOS),linux darwin))

README.md

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,26 @@ To use the multi-target functionality, send an http request to the endpoint `/pr
3030

3131
To avoid putting sensitive information like username and password in the URL, preconfigured auth modules are supported via the [auth_modules](#auth_modules) section of the config file. auth_modules for DSNs can be used with the `/probe` endpoint by specifying the `?auth_module=foo` http parameter.
3232

33+
Example Prometheus config:
34+
```yaml
35+
scrape_configs:
36+
- job_name: 'postgres'
37+
static_configs:
38+
- targets:
39+
- server1:5432
40+
- server2:5432
41+
metrics_path: /probe
42+
params:
43+
auth_module: [foo]
44+
relabel_configs:
45+
- source_labels: [__address__]
46+
target_label: __param_target
47+
- source_labels: [__param_target]
48+
target_label: instance
49+
- target_label: __address__
50+
replacement: 127.0.0.1:9116 # The postgres exporter's real hostname:port.
51+
```
52+
3353
## Configuration File
3454
3555
The configuration file controls the behavior of the exporter. It can be set using the `--config.file` command line flag and defaults to `postgres_exporter.yml`.
@@ -73,7 +93,10 @@ This will build the docker image as `prometheuscommunity/postgres_exporter:${bra
7393

7494

7595
* `[no-]collector.database`
76-
Enable the database collector (default: enabled).
96+
Enable the `database` collector (default: enabled).
97+
98+
* `[no-]collector.locks`
99+
Enable the `locks` collector (default: enabled).
77100

78101
* `[no-]collector.postmaster`
79102
Enable the `postmaster` collector (default: enabled).

VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
0.13.0
1+
0.13.1

cmd/postgres_exporter/postgres_exporter.go

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -176,15 +176,6 @@ var builtinMetricMaps = map[string]intermediateMetricMap{
176176
true,
177177
0,
178178
},
179-
"pg_locks": {
180-
map[string]ColumnMapping{
181-
"datname": {LABEL, "Name of this database", nil, nil},
182-
"mode": {LABEL, "Type of Lock", nil, nil},
183-
"count": {GAUGE, "Number of locks", nil, nil},
184-
},
185-
true,
186-
0,
187-
},
188179
"pg_stat_replication": {
189180
map[string]ColumnMapping{
190181
"procpid": {DISCARD, "Process ID of a WAL sender process", nil, semver.MustParseRange("<9.2.0")},

cmd/postgres_exporter/queries.go

Lines changed: 0 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -46,31 +46,6 @@ type OverrideQuery struct {
4646
// Overriding queries for namespaces above.
4747
// TODO: validate this is a closed set in tests, and there are no overlaps
4848
var queryOverrides = map[string][]OverrideQuery{
49-
"pg_locks": {
50-
{
51-
semver.MustParseRange(">0.0.0"),
52-
`SELECT pg_database.datname,tmp.mode,COALESCE(count,0) as count
53-
FROM
54-
(
55-
VALUES ('accesssharelock'),
56-
('rowsharelock'),
57-
('rowexclusivelock'),
58-
('shareupdateexclusivelock'),
59-
('sharelock'),
60-
('sharerowexclusivelock'),
61-
('exclusivelock'),
62-
('accessexclusivelock'),
63-
('sireadlock')
64-
) AS tmp(mode) CROSS JOIN pg_database
65-
LEFT JOIN
66-
(SELECT database, lower(mode) AS mode,count(*) AS count
67-
FROM pg_locks WHERE database IS NOT NULL
68-
GROUP BY database, lower(mode)
69-
) AS tmp2
70-
ON tmp.mode=tmp2.mode and pg_database.oid = tmp2.database ORDER BY 1`,
71-
},
72-
},
73-
7449
"pg_stat_replication": {
7550
{
7651
semver.MustParseRange(">=10.0.0"),

collector/collector_test.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,14 @@ func readMetric(m prometheus.Metric) MetricResult {
4949
func sanitizeQuery(q string) string {
5050
q = strings.Join(strings.Fields(q), " ")
5151
q = strings.Replace(q, "(", "\\(", -1)
52+
q = strings.Replace(q, "?", "\\?", -1)
5253
q = strings.Replace(q, ")", "\\)", -1)
54+
q = strings.Replace(q, "[", "\\[", -1)
55+
q = strings.Replace(q, "]", "\\]", -1)
56+
q = strings.Replace(q, "{", "\\{", -1)
57+
q = strings.Replace(q, "}", "\\}", -1)
5358
q = strings.Replace(q, "*", "\\*", -1)
59+
q = strings.Replace(q, "^", "\\^", -1)
5460
q = strings.Replace(q, "$", "\\$", -1)
5561
return q
5662
}

collector/pg_database.go

Lines changed: 14 additions & 9 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"
@@ -79,38 +80,42 @@ func (c PGDatabaseCollector) Update(ctx context.Context, instance *instance, ch
7980
var databases []string
8081

8182
for rows.Next() {
82-
var datname string
83+
var datname sql.NullString
8384
if err := rows.Scan(&datname); err != nil {
8485
return err
8586
}
8687

88+
if !datname.Valid {
89+
continue
90+
}
8791
// Ignore excluded databases
8892
// Filtering is done here instead of in the query to avoid
8993
// a complicated NOT IN query with a variable number of parameters
90-
if sliceContains(c.excludedDatabases, datname) {
94+
if sliceContains(c.excludedDatabases, datname.String) {
9195
continue
9296
}
9397

94-
databases = append(databases, datname)
98+
databases = append(databases, datname.String)
9599
}
96100

97101
// Query the size of the databases
98102
for _, datname := range databases {
99-
var size int64
103+
var size sql.NullFloat64
100104
err = db.QueryRowContext(ctx, pgDatabaseSizeQuery, datname).Scan(&size)
101105
if err != nil {
102106
return err
103107
}
104108

109+
sizeMetric := 0.0
110+
if size.Valid {
111+
sizeMetric = size.Float64
112+
}
105113
ch <- prometheus.MustNewConstMetric(
106114
pgDatabaseSizeDesc,
107-
prometheus.GaugeValue, float64(size), datname,
115+
prometheus.GaugeValue, sizeMetric, datname,
108116
)
109117
}
110-
if err := rows.Err(); err != nil {
111-
return err
112-
}
113-
return nil
118+
return rows.Err()
114119
}
115120

116121
func sliceContains(slice []string, s string) bool {

collector/pg_database_test.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,3 +59,43 @@ func TestPGDatabaseCollector(t *testing.T) {
5959
t.Errorf("there were unfulfilled exceptions: %s", err)
6060
}
6161
}
62+
63+
// TODO add a null db test
64+
65+
func TestPGDatabaseCollectorNullMetric(t *testing.T) {
66+
db, mock, err := sqlmock.New()
67+
if err != nil {
68+
t.Fatalf("Error opening a stub db connection: %s", err)
69+
}
70+
defer db.Close()
71+
72+
inst := &instance{db: db}
73+
74+
mock.ExpectQuery(sanitizeQuery(pgDatabaseQuery)).WillReturnRows(sqlmock.NewRows([]string{"datname"}).
75+
AddRow("postgres"))
76+
77+
mock.ExpectQuery(sanitizeQuery(pgDatabaseSizeQuery)).WithArgs("postgres").WillReturnRows(sqlmock.NewRows([]string{"pg_database_size"}).
78+
AddRow(nil))
79+
80+
ch := make(chan prometheus.Metric)
81+
go func() {
82+
defer close(ch)
83+
c := PGDatabaseCollector{}
84+
if err := c.Update(context.Background(), inst, ch); err != nil {
85+
t.Errorf("Error calling PGDatabaseCollector.Update: %s", err)
86+
}
87+
}()
88+
89+
expected := []MetricResult{
90+
{labels: labelMap{"datname": "postgres"}, value: 0, metricType: dto.MetricType_GAUGE},
91+
}
92+
convey.Convey("Metrics comparison", t, func() {
93+
for _, expect := range expected {
94+
m := readMetric(<-ch)
95+
convey.So(expect, convey.ShouldResemble, m)
96+
}
97+
})
98+
if err := mock.ExpectationsWereMet(); err != nil {
99+
t.Errorf("there were unfulfilled exceptions: %s", err)
100+
}
101+
}

collector/pg_database_wraparound.go

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
// Copyright 2023 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 collector
15+
16+
import (
17+
"context"
18+
"database/sql"
19+
20+
"github.com/go-kit/log"
21+
"github.com/go-kit/log/level"
22+
"github.com/prometheus/client_golang/prometheus"
23+
)
24+
25+
const databaseWraparoundSubsystem = "database_wraparound"
26+
27+
func init() {
28+
registerCollector(databaseWraparoundSubsystem, defaultDisabled, NewPGDatabaseWraparoundCollector)
29+
}
30+
31+
type PGDatabaseWraparoundCollector struct {
32+
log log.Logger
33+
}
34+
35+
func NewPGDatabaseWraparoundCollector(config collectorConfig) (Collector, error) {
36+
return &PGDatabaseWraparoundCollector{log: config.logger}, nil
37+
}
38+
39+
var (
40+
databaseWraparoundAgeDatfrozenxid = prometheus.NewDesc(
41+
prometheus.BuildFQName(namespace, databaseWraparoundSubsystem, "age_datfrozenxid_seconds"),
42+
"Age of the oldest transaction ID that has not been frozen.",
43+
[]string{"datname"},
44+
prometheus.Labels{},
45+
)
46+
databaseWraparoundAgeDatminmxid = prometheus.NewDesc(
47+
prometheus.BuildFQName(namespace, databaseWraparoundSubsystem, "age_datminmxid_seconds"),
48+
"Age of the oldest multi-transaction ID that has been replaced with a transaction ID.",
49+
[]string{"datname"},
50+
prometheus.Labels{},
51+
)
52+
53+
databaseWraparoundQuery = `
54+
SELECT
55+
datname,
56+
age(d.datfrozenxid) as age_datfrozenxid,
57+
mxid_age(d.datminmxid) as age_datminmxid
58+
FROM
59+
pg_catalog.pg_database d
60+
WHERE
61+
d.datallowconn
62+
`
63+
)
64+
65+
func (c *PGDatabaseWraparoundCollector) Update(ctx context.Context, instance *instance, ch chan<- prometheus.Metric) error {
66+
db := instance.getDB()
67+
rows, err := db.QueryContext(ctx,
68+
databaseWraparoundQuery)
69+
70+
if err != nil {
71+
return err
72+
}
73+
defer rows.Close()
74+
75+
for rows.Next() {
76+
var datname sql.NullString
77+
var ageDatfrozenxid, ageDatminmxid sql.NullFloat64
78+
79+
if err := rows.Scan(&datname, &ageDatfrozenxid, &ageDatminmxid); err != nil {
80+
return err
81+
}
82+
83+
if !datname.Valid {
84+
level.Debug(c.log).Log("msg", "Skipping database with NULL name")
85+
continue
86+
}
87+
if !ageDatfrozenxid.Valid {
88+
level.Debug(c.log).Log("msg", "Skipping stat emission with NULL age_datfrozenxid")
89+
continue
90+
}
91+
if !ageDatminmxid.Valid {
92+
level.Debug(c.log).Log("msg", "Skipping stat emission with NULL age_datminmxid")
93+
continue
94+
}
95+
96+
ageDatfrozenxidMetric := ageDatfrozenxid.Float64
97+
98+
ch <- prometheus.MustNewConstMetric(
99+
databaseWraparoundAgeDatfrozenxid,
100+
prometheus.GaugeValue,
101+
ageDatfrozenxidMetric, datname.String,
102+
)
103+
104+
ageDatminmxidMetric := ageDatminmxid.Float64
105+
ch <- prometheus.MustNewConstMetric(
106+
databaseWraparoundAgeDatminmxid,
107+
prometheus.GaugeValue,
108+
ageDatminmxidMetric, datname.String,
109+
)
110+
}
111+
if err := rows.Err(); err != nil {
112+
return err
113+
}
114+
return nil
115+
}

0 commit comments

Comments
 (0)