Skip to content

Commit 286550f

Browse files
authored
Merge pull request #21 from ncabatoff/feature-queries_from_file
Feature queries from file
2 parents 935ca79 + 511ef0e commit 286550f

20 files changed

+9835
-1
lines changed

README.md

+4
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

+128-1
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,14 @@ import (
44
"database/sql"
55
"flag"
66
"fmt"
7+
"io/ioutil"
78
"math"
89
"net/http"
910
"os"
11+
"strconv"
1012
"time"
1113

12-
"strconv"
14+
"gopkg.in/yaml.v2"
1315

1416
_ "github.com/lib/pq"
1517
"github.com/prometheus/client_golang/prometheus"
@@ -27,6 +29,14 @@ var (
2729
"web.telemetry-path", "/metrics",
2830
"Path under which to expose metrics.",
2931
)
32+
queriesPath = flag.String(
33+
"extend.query-path", "",
34+
"Path to custom queries to run.",
35+
)
36+
onlyDumpMaps = flag.Bool(
37+
"dumpmaps", false,
38+
"Do not run, simply dump the maps.",
39+
)
3040
)
3141

3242
// Metric name parts.
@@ -102,6 +112,21 @@ var variableMaps = map[string]map[string]ColumnMapping{
102112
},
103113
}
104114

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+
105130
var metricMaps = map[string]map[string]ColumnMapping{
106131
"pg_stat_bgwriter": map[string]ColumnMapping{
107132
"checkpoints_timed": {COUNTER, "Number of scheduled checkpoints that have been performed", nil},
@@ -223,6 +248,68 @@ var queryOverrides = map[string]string{
223248
ON tmp.state = tmp2.state AND pg_database.datname = tmp2.datname`,
224249
}
225250

251+
// Add queries to the metricMaps and queryOverrides maps
252+
func addQueries(queriesPath string) (err error) {
253+
var extra map[string]interface{}
254+
255+
content, err := ioutil.ReadFile(queriesPath)
256+
if err != nil {
257+
return err
258+
}
259+
260+
err = yaml.Unmarshal(content, &extra)
261+
if err != nil {
262+
return err
263+
}
264+
265+
for metric, specs := range extra {
266+
for key, value := range specs.(map[interface{}]interface{}) {
267+
switch key.(string) {
268+
case "query":
269+
query := value.(string)
270+
queryOverrides[metric] = query
271+
272+
case "metrics":
273+
for _, c := range value.([]interface{}) {
274+
column := c.(map[interface{}]interface{})
275+
276+
for n, a := range column {
277+
var cmap ColumnMapping
278+
279+
metric_map, ok := metricMaps[metric]
280+
if !ok {
281+
metric_map = make(map[string]ColumnMapping)
282+
}
283+
284+
name := n.(string)
285+
286+
for attr_key, attr_val := range a.(map[interface{}]interface{}) {
287+
switch attr_key.(string) {
288+
case "usage":
289+
usage, err := stringToColumnUsage(attr_val.(string))
290+
if err != nil {
291+
return err
292+
}
293+
cmap.usage = usage
294+
case "description":
295+
cmap.description = attr_val.(string)
296+
}
297+
}
298+
299+
cmap.mapping = nil
300+
301+
metric_map[name] = cmap
302+
303+
metricMaps[metric] = metric_map
304+
}
305+
}
306+
}
307+
}
308+
}
309+
310+
return
311+
}
312+
226313
// Turn the MetricMap column mapping into a prometheus descriptor mapping.
227314
func makeDescMap(metricMaps map[string]map[string]ColumnMapping) map[string]MetricMapNamespace {
228315
var metricMap = make(map[string]MetricMapNamespace)
@@ -317,6 +404,34 @@ func makeDescMap(metricMaps map[string]map[string]ColumnMapping) map[string]Metr
317404
return metricMap
318405
}
319406

407+
// convert a string to the corresponding ColumnUsage
408+
func stringToColumnUsage(s string) (u ColumnUsage, err error) {
409+
switch s {
410+
case "DISCARD":
411+
u = DISCARD
412+
413+
case "LABEL":
414+
u = LABEL
415+
416+
case "COUNTER":
417+
u = COUNTER
418+
419+
case "GAUGE":
420+
u = GAUGE
421+
422+
case "MAPPEDMETRIC":
423+
u = MAPPEDMETRIC
424+
425+
case "DURATION":
426+
u = DURATION
427+
428+
default:
429+
err = fmt.Errorf("wrong ColumnUsage given : %s", s)
430+
}
431+
432+
return
433+
}
434+
320435
// Convert database.sql types to float64s for Prometheus consumption. Null types are mapped to NaN. string and []byte
321436
// types are mapped as NaN and !ok
322437
func dbToFloat64(t interface{}) (float64, bool) {
@@ -590,6 +705,18 @@ func (e *Exporter) scrape(ch chan<- prometheus.Metric) {
590705
func main() {
591706
flag.Parse()
592707

708+
if *queriesPath != "" {
709+
err := addQueries(*queriesPath)
710+
if err != nil {
711+
log.Warnln("Unparseable queries file - discarding merge: ", *queriesPath, err)
712+
}
713+
}
714+
715+
if *onlyDumpMaps {
716+
dumpMaps()
717+
return
718+
}
719+
593720
dsn := os.Getenv("DATA_SOURCE_NAME")
594721
if len(dsn) == 0 {
595722
log.Fatal("couldn't find environment variable DATA_SOURCE_NAME")

queries.yaml

+100
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
pg_replication:
2+
query: "SELECT EXTRACT(EPOCH FROM (now() - pg_last_xact_replay_timestamp()))::INT as lag"
3+
metrics:
4+
- lag:
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)