Skip to content

Commit 298b142

Browse files
authored
Merge pull request #1 from ncabatoff/feature-queries_from_file
fix bug with multiple columns in same metric
2 parents 895166b + 511ef0e commit 298b142

File tree

16 files changed

+690
-244
lines changed

16 files changed

+690
-244
lines changed

.travis.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ go:
77
script:
88
- make all
99
- make docker
10+
- make test-integration
1011
after_success:
1112
- if [ "$TRAVIS_BRANCH" == "master" ]; then docker login -e $DOCKER_EMAIL -u $DOCKER_USER
1213
-p $DOCKER_PASS ; docker push wrouesnel/postgres_exporter ; fi

Makefile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ vet:
2121
test:
2222
go test -v .
2323

24+
test-integration:
25+
tests/test-smoke
26+
2427
# Do a self-contained docker build - we pull the official upstream container,
2528
# then template out a dockerfile which builds the real image.
2629
docker-build: postgres_exporter

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,10 @@ for l in StringIO(x):
6565
Adjust the value of the resultant prometheus value type appropriately. This helps build
6666
rich self-documenting metrics for the exporter.
6767

68+
### Adding new metrics via a config file
69+
70+
The -extend.query-path command-line argument specifies a YAML file containing additional queries to run.
71+
Some examples are provided in [queries.yaml](queries.yaml).
6872

6973
### Running as non-superuser
7074

postgres_exporter.go

Lines changed: 56 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,24 @@
11
package main
22

33
import (
4-
//"bytes"
54
"database/sql"
65
"flag"
76
"fmt"
7+
"io/ioutil"
8+
"math"
89
"net/http"
910
"os"
10-
//"regexp"
11-
//"strconv"
12-
//"strings"
13-
"math"
11+
"strconv"
1412
"time"
15-
"io/ioutil"
13+
1614
"gopkg.in/yaml.v2"
1715

1816
_ "github.com/lib/pq"
1917
"github.com/prometheus/client_golang/prometheus"
20-
"github.com/prometheus/log"
21-
"strconv"
18+
"github.com/prometheus/common/log"
2219
)
2320

24-
var Version string = "0.0.0-dev"
21+
var Version string = "0.0.1"
2522

2623
var (
2724
listenAddress = flag.String(
@@ -36,6 +33,10 @@ var (
3633
"extend.query-path", "",
3734
"Path to custom queries to run.",
3835
)
36+
onlyDumpMaps = flag.Bool(
37+
"dumpmaps", false,
38+
"Do not run, simply dump the maps.",
39+
)
3940
)
4041

4142
// Metric name parts.
@@ -111,6 +112,21 @@ var variableMaps = map[string]map[string]ColumnMapping{
111112
},
112113
}
113114

115+
func dumpMaps() {
116+
for name, cmap := range metricMaps {
117+
query, ok := queryOverrides[name]
118+
if ok {
119+
fmt.Printf("%s: %s\n", name, query)
120+
} else {
121+
fmt.Println(name)
122+
}
123+
for column, details := range cmap {
124+
fmt.Printf(" %-40s %v\n", column, details)
125+
}
126+
fmt.Println()
127+
}
128+
}
129+
114130
var metricMaps = map[string]map[string]ColumnMapping{
115131
"pg_stat_bgwriter": map[string]ColumnMapping{
116132
"checkpoints_timed": {COUNTER, "Number of scheduled checkpoints that have been performed", nil},
@@ -235,7 +251,6 @@ func addQueries(queriesPath string) (err error) {
235251
return err
236252
}
237253

238-
239254
for metric, specs := range extra {
240255
for key, value := range specs.(map[interface{}]interface{}) {
241256
switch key.(string) {
@@ -249,14 +264,16 @@ func addQueries(queriesPath string) (err error) {
249264

250265
for n, a := range column {
251266
var cmap ColumnMapping
252-
var metric_map map[string]ColumnMapping
253267

254-
metric_map = make(map[string]ColumnMapping)
268+
metric_map, ok := metricMaps[metric]
269+
if !ok {
270+
metric_map = make(map[string]ColumnMapping)
271+
}
255272

256273
name := n.(string)
257274

258275
for attr_key, attr_val := range a.(map[interface{}]interface{}) {
259-
switch(attr_key.(string)) {
276+
switch attr_key.(string) {
260277
case "usage":
261278
usage, err := stringToColumnUsage(attr_val.(string))
262279
if err != nil {
@@ -355,6 +372,10 @@ func makeDescMap(metricMaps map[string]map[string]ColumnMapping) map[string]Metr
355372
return math.NaN(), false
356373
}
357374

375+
if durationString == "-1" {
376+
return math.NaN(), false
377+
}
378+
358379
d, err := time.ParseDuration(durationString)
359380
if err != nil {
360381
log.Errorln("Failed converting result to metric:", columnName, in, err)
@@ -374,7 +395,7 @@ func makeDescMap(metricMaps map[string]map[string]ColumnMapping) map[string]Metr
374395

375396
// convert a string to the corresponding ColumnUsage
376397
func stringToColumnUsage(s string) (u ColumnUsage, err error) {
377-
switch(s) {
398+
switch s {
378399
case "DISCARD":
379400
u = DISCARD
380401

@@ -418,6 +439,13 @@ func dbToFloat64(t interface{}) (float64, bool) {
418439
return math.NaN(), false
419440
}
420441
return result, true
442+
case string:
443+
result, err := strconv.ParseFloat(v, 64)
444+
if err != nil {
445+
log.Infoln("Could not parse string:", err)
446+
return math.NaN(), false
447+
}
448+
return result, true
421449
case nil:
422450
return math.NaN(), true
423451
default:
@@ -536,7 +564,7 @@ func (e *Exporter) scrape(ch chan<- prometheus.Metric) {
536564

537565
db, err := sql.Open("postgres", e.dsn)
538566
if err != nil {
539-
log.Println("Error opening connection to database:", err)
567+
log.Infoln("Error opening connection to database:", err)
540568
e.error.Set(1)
541569
return
542570
}
@@ -582,7 +610,7 @@ func (e *Exporter) scrape(ch chan<- prometheus.Metric) {
582610
// Don't fail on a bad scrape of one metric
583611
rows, err := db.Query(query)
584612
if err != nil {
585-
log.Println("Error running query on database: ", namespace, err)
613+
log.Infoln("Error running query on database: ", namespace, err)
586614
e.error.Set(1)
587615
return
588616
}
@@ -591,7 +619,7 @@ func (e *Exporter) scrape(ch chan<- prometheus.Metric) {
591619
var columnNames []string
592620
columnNames, err = rows.Columns()
593621
if err != nil {
594-
log.Println("Error retrieving column list for: ", namespace, err)
622+
log.Infoln("Error retrieving column list for: ", namespace, err)
595623
e.error.Set(1)
596624
return
597625
}
@@ -611,7 +639,7 @@ func (e *Exporter) scrape(ch chan<- prometheus.Metric) {
611639
for rows.Next() {
612640
err = rows.Scan(scanArgs...)
613641
if err != nil {
614-
log.Println("Error retrieving rows:", namespace, err)
642+
log.Infoln("Error retrieving rows:", namespace, err)
615643
e.error.Set(1)
616644
return
617645
}
@@ -666,18 +694,23 @@ func (e *Exporter) scrape(ch chan<- prometheus.Metric) {
666694
func main() {
667695
flag.Parse()
668696

669-
dsn := os.Getenv("DATA_SOURCE_NAME")
670-
if len(dsn) == 0 {
671-
log.Fatal("couldn't find environment variable DATA_SOURCE_NAME")
672-
}
673-
674697
if *queriesPath != "" {
675698
err := addQueries(*queriesPath)
676699
if err != nil {
677700
log.Warnln("Unparseable queries file - discarding merge: ", *queriesPath, err)
678701
}
679702
}
680703

704+
if *onlyDumpMaps {
705+
dumpMaps()
706+
return
707+
}
708+
709+
dsn := os.Getenv("DATA_SOURCE_NAME")
710+
if len(dsn) == 0 {
711+
log.Fatal("couldn't find environment variable DATA_SOURCE_NAME")
712+
}
713+
681714
exporter := NewExporter(dsn)
682715
prometheus.MustRegister(exporter)
683716

queries.yaml

Lines changed: 97 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,100 @@
11
pg_replication:
2-
query: 'SELECT EXTRACT(EPOCH FROM (now() - pg_last_xact_replay_timestamp()))::INT as lag'
2+
query: "SELECT EXTRACT(EPOCH FROM (now() - pg_last_xact_replay_timestamp()))::INT as lag"
33
metrics:
44
- lag:
5-
usage: 'GAUGE'
6-
description: 'Replication lag behind master in seconds'
5+
usage: "GAUGE"
6+
description: "Replication lag behind master in seconds"
7+
8+
pg_postmaster:
9+
query: "SELECT pg_postmaster_start_time as start_time_seconds from pg_postmaster_start_time()"
10+
metrics:
11+
- start_time_seconds:
12+
usage: "GAUGE"
13+
description: "Time at which postmaster started"
14+
15+
pg_settings_shared_buffers:
16+
query: "SELECT 8192*setting::int as bytes from pg_settings where name = 'shared_buffers'"
17+
metrics:
18+
- bytes:
19+
usage: "GAUGE"
20+
description: "Size of shared_buffers"
21+
22+
pg_settings_checkpoint:
23+
query: "select (select setting::int from pg_settings where name = 'checkpoint_segments') as segments, (select setting::int from pg_settings where name = 'checkpoint_timeout') as timeout_seconds, (select setting::float from pg_settings where name = 'checkpoint_completion_target') as completion_target"
24+
metrics:
25+
- segments:
26+
usage: "GAUGE"
27+
description: "Number of checkpoint segments"
28+
- timeout_seconds:
29+
usage: "GAUGE"
30+
description: "Checkpoint timeout in seconds"
31+
- completion_target:
32+
usage: "GAUGE"
33+
description: "Checkpoint completion target, ranging from 0 to 1"
34+
35+
pg_stat_user_tables:
36+
query: "SELECT schemaname, relname, seq_scan, seq_tup_read, idx_scan, idx_tup_fetch, n_tup_ins, n_tup_upd, n_tup_del, n_tup_hot_upd, n_live_tup, n_dead_tup, n_mod_since_analyze, last_vacuum, last_autovacuum, last_analyze, last_autoanalyze, vacuum_count, autovacuum_count, analyze_count, autoanalyze_count FROM pg_stat_user_tables"
37+
metrics:
38+
- schemaname:
39+
usage: "LABEL"
40+
description: "Name of the schema that this table is in"
41+
- relname:
42+
usage: "LABEL"
43+
description: "Name of this table"
44+
- seq_scan:
45+
usage: "COUNTER"
46+
description: "Number of sequential scans initiated on this table"
47+
- seq_tup_read:
48+
usage: "COUNTER"
49+
description: "Number of live rows fetched by sequential scans"
50+
- idx_scan:
51+
usage: "COUNTER"
52+
description: "Number of index scans initiated on this table"
53+
- idx_tup_fetch:
54+
usage: "COUNTER"
55+
description: "Number of live rows fetched by index scans"
56+
- n_tup_ins:
57+
usage: "COUNTER"
58+
description: "Number of rows inserted"
59+
- n_tup_upd:
60+
usage: "COUNTER"
61+
description: "Number of rows updated"
62+
- n_tup_del:
63+
usage: "COUNTER"
64+
description: "Number of rows deleted"
65+
- n_tup_hot_upd:
66+
usage: "COUNTER"
67+
description: "Number of rows HOT updated (i.e., with no separate index update required)"
68+
- n_live_tup:
69+
usage: "GAUGE"
70+
description: "Estimated number of live rows"
71+
- n_dead_tup:
72+
usage: "GAUGE"
73+
description: "Estimated number of dead rows"
74+
- n_mod_since_analyze:
75+
usage: "GAUGE"
76+
description: "Estimated number of rows changed since last analyze"
77+
- last_vacuum:
78+
usage: "GAUGE"
79+
description: "Last time at which this table was manually vacuumed (not counting VACUUM FULL)"
80+
- last_autovacuum:
81+
usage: "GAUGE"
82+
description: "Last time at which this table was vacuumed by the autovacuum daemon"
83+
- last_analyze:
84+
usage: "GAUGE"
85+
description: "Last time at which this table was manually analyzed"
86+
- last_autoanalyze:
87+
usage: "GAUGE"
88+
description: "Last time at which this table was analyzed by the autovacuum daemon"
89+
- vacuum_count:
90+
usage: "COUNTER"
91+
description: "Number of times this table has been manually vacuumed (not counting VACUUM FULL)"
92+
- autovacuum_count:
93+
usage: "COUNTER"
94+
description: "Number of times this table has been vacuumed by the autovacuum daemon"
95+
- analyze_count:
96+
usage: "COUNTER"
97+
description: "Number of times this table has been manually analyzed"
98+
- autoanalyze_count:
99+
usage: "COUNTER"
100+
description: "Number of times this table has been analyzed by the autovacuum daemon"

0 commit comments

Comments
 (0)