mirror of
https://github.com/prometheus-community/postgres_exporter
synced 2025-04-25 04:28:01 +00:00
Re-enabled the YAML file format processing.
This commit is contained in:
parent
2297eb5ba8
commit
d49127d34f
@ -4,12 +4,13 @@ services:
|
|||||||
language: go
|
language: go
|
||||||
go:
|
go:
|
||||||
- '1.7'
|
- '1.7'
|
||||||
# Make sure we have p2
|
# Make sure we have p2 and the postgres client.
|
||||||
before_install:
|
before_install:
|
||||||
- sudo wget -O /usr/local/bin/p2 https://github.com/wrouesnel/p2cli/releases/download/r4/p2 &&
|
- sudo wget -O /usr/local/bin/p2 https://github.com/wrouesnel/p2cli/releases/download/r4/p2 &&
|
||||||
sudo chmod +x /usr/local/bin/p2
|
sudo chmod +x /usr/local/bin/p2
|
||||||
- sudo wget -O /usr/local/bin/docker-compose https://github.com/docker/compose/releases/download/1.9.0-rc4/docker-compose-Linux-x86_64 &&
|
- sudo wget -O /usr/local/bin/docker-compose https://github.com/docker/compose/releases/download/1.9.0-rc4/docker-compose-Linux-x86_64 &&
|
||||||
sudo chmod +x /usr/local/bin/docker-compose
|
sudo chmod +x /usr/local/bin/docker-compose
|
||||||
|
- sudo apt-get update && apt-get install postgresql-client-common
|
||||||
|
|
||||||
script:
|
script:
|
||||||
- make all
|
- make all
|
||||||
|
@ -4,7 +4,7 @@ import (
|
|||||||
"database/sql"
|
"database/sql"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
//"io/ioutil"
|
"io/ioutil"
|
||||||
"math"
|
"math"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
@ -13,7 +13,7 @@ import (
|
|||||||
"regexp"
|
"regexp"
|
||||||
"errors"
|
"errors"
|
||||||
|
|
||||||
//"gopkg.in/yaml.v2"
|
"gopkg.in/yaml.v2"
|
||||||
|
|
||||||
_ "github.com/lib/pq"
|
_ "github.com/lib/pq"
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
@ -41,10 +41,6 @@ var (
|
|||||||
"dumpmaps", false,
|
"dumpmaps", false,
|
||||||
"Do not run, simply dump the maps.",
|
"Do not run, simply dump the maps.",
|
||||||
)
|
)
|
||||||
expectReplicationStats = flag.Bool(
|
|
||||||
"config.expect-replication-stats", false,
|
|
||||||
"The target database has replication configured, log missing replication stats as an error.",
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Metric name parts.
|
// Metric name parts.
|
||||||
@ -71,6 +67,22 @@ var landingPage = []byte(`<html>
|
|||||||
|
|
||||||
type ColumnUsage int
|
type ColumnUsage int
|
||||||
|
|
||||||
|
// Implements the yaml.Unmarshaller interface
|
||||||
|
func (this *ColumnUsage) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||||
|
var value string
|
||||||
|
if err := unmarshal(&value); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
columnUsage, err := stringToColumnUsage(value)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
*this = columnUsage
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
DISCARD ColumnUsage = iota // Ignore this column
|
DISCARD ColumnUsage = iota // Ignore this column
|
||||||
LABEL ColumnUsage = iota // Use this column as a label
|
LABEL ColumnUsage = iota // Use this column as a label
|
||||||
@ -103,10 +115,18 @@ func parseVersion(versionString string) (semver.Version, error) {
|
|||||||
|
|
||||||
// User-friendly representation of a prometheus descriptor map
|
// User-friendly representation of a prometheus descriptor map
|
||||||
type ColumnMapping struct {
|
type ColumnMapping struct {
|
||||||
usage ColumnUsage
|
usage ColumnUsage `yaml:"usage"`
|
||||||
description string
|
description string `yaml:"description"`
|
||||||
mapping map[string]float64 // Optional column mapping for MAPPEDMETRIC
|
mapping map[string]float64 `yaml:"metric_mapping"` // Optional column mapping for MAPPEDMETRIC
|
||||||
supportedVersions semver.Range // Semantic version ranges which are supported. Unsupported columns are not queried (internally converted to DISCARD).
|
supportedVersions semver.Range `yaml:"pg_version"` // Semantic version ranges which are supported. Unsupported columns are not queried (internally converted to DISCARD).
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *ColumnMapping) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||||
|
type plain ColumnMapping
|
||||||
|
if err := unmarshal((*plain)(this)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Groups metric maps under a shared set of labels
|
// Groups metric maps under a shared set of labels
|
||||||
@ -364,67 +384,110 @@ func makeQueryOverrideMap(pgVersion semver.Version, queryOverrides map[string][]
|
|||||||
return resultMap
|
return resultMap
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add queries to the metricMaps and queryOverrides maps
|
// Add queries to the metricMaps and queryOverrides maps. Added queries do not
|
||||||
//func addQueries(queriesPath string) (err error) {
|
// respect version requirements, because it is assumed that the user knows
|
||||||
// var extra map[string]interface{}
|
// what they are doing with their version of postgres.
|
||||||
//
|
//
|
||||||
// content, err := ioutil.ReadFile(queriesPath)
|
// This function modifies metricMap and queryOverrideMap to contain the new
|
||||||
// if err != nil {
|
// queries.
|
||||||
// return err
|
// TODO: test code for all this.
|
||||||
// }
|
// TODO: use proper struct type system
|
||||||
//
|
// TODO: the YAML this supports is "non-standard" - we should move away from it.
|
||||||
// err = yaml.Unmarshal(content, &extra)
|
func addQueries(queriesPath string, pgVersion semver.Version, exporterMap map[string]MetricMapNamespace, queryOverrideMap map[string]string) error {
|
||||||
// if err != nil {
|
var extra map[string]interface{}
|
||||||
// return err
|
|
||||||
// }
|
content, err := ioutil.ReadFile(queriesPath)
|
||||||
//
|
if err != nil {
|
||||||
// for metric, specs := range extra {
|
return err
|
||||||
// for key, value := range specs.(map[interface{}]interface{}) {
|
}
|
||||||
// switch key.(string) {
|
|
||||||
// case "query":
|
err = yaml.Unmarshal(content, &extra)
|
||||||
// query := value.(string)
|
if err != nil {
|
||||||
// queryOverrides[metric] = query
|
return err
|
||||||
//
|
}
|
||||||
// case "metrics":
|
|
||||||
// for _, c := range value.([]interface{}) {
|
// Stores the loaded map representation
|
||||||
// column := c.(map[interface{}]interface{})
|
metricMaps := make(map[string]map[string]ColumnMapping)
|
||||||
//
|
newQueryOverrides := make(map[string]string)
|
||||||
// for n, a := range column {
|
|
||||||
// var cmap ColumnMapping
|
for metric, specs := range extra {
|
||||||
//
|
log.Debugln("New user metric namespace from YAML:", metric)
|
||||||
// metric_map, ok := metricMaps[metric]
|
for key, value := range specs.(map[interface{}]interface{}) {
|
||||||
// if !ok {
|
switch key.(string) {
|
||||||
// metric_map = make(map[string]ColumnMapping)
|
case "query":
|
||||||
// }
|
query := value.(string)
|
||||||
//
|
newQueryOverrides[metric] = query
|
||||||
// name := n.(string)
|
|
||||||
//
|
case "metrics":
|
||||||
// for attr_key, attr_val := range a.(map[interface{}]interface{}) {
|
for _, c := range value.([]interface{}) {
|
||||||
// switch attr_key.(string) {
|
column := c.(map[interface{}]interface{})
|
||||||
// case "usage":
|
|
||||||
// usage, err := stringToColumnUsage(attr_val.(string))
|
for n, a := range column {
|
||||||
// if err != nil {
|
var columnMapping ColumnMapping
|
||||||
// return err
|
|
||||||
// }
|
// Fetch the metric map we want to work on.
|
||||||
// cmap.usage = usage
|
metricMap, ok := metricMaps[metric]
|
||||||
// case "description":
|
if !ok {
|
||||||
// cmap.description = attr_val.(string)
|
// Namespace for metric not found - add it.
|
||||||
// }
|
metricMap = make(map[string]ColumnMapping)
|
||||||
// }
|
metricMaps[metric] = metricMap
|
||||||
//
|
}
|
||||||
// cmap.mapping = nil
|
|
||||||
//
|
// Get name.
|
||||||
// metric_map[name] = cmap
|
name := n.(string)
|
||||||
//
|
|
||||||
// metricMaps[metric] = metric_map
|
for attrKey, attrVal := range a.(map[interface{}]interface{}) {
|
||||||
// }
|
switch attrKey.(string) {
|
||||||
// }
|
case "usage":
|
||||||
// }
|
usage, err := stringToColumnUsage(attrVal.(string))
|
||||||
// }
|
if err != nil {
|
||||||
// }
|
return err
|
||||||
//
|
}
|
||||||
// return
|
columnMapping.usage = usage
|
||||||
//}
|
case "description":
|
||||||
|
columnMapping.description = attrVal.(string)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: we should support this
|
||||||
|
columnMapping.mapping = nil
|
||||||
|
// Should we support this for users?
|
||||||
|
columnMapping.supportedVersions = nil
|
||||||
|
|
||||||
|
metricMap[name] = columnMapping
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert the loaded metric map into exporter representation
|
||||||
|
partialExporterMap := makeDescMap(pgVersion, metricMaps)
|
||||||
|
|
||||||
|
// Merge the two maps (which are now quite flatteend)
|
||||||
|
for k, v := range partialExporterMap {
|
||||||
|
_, found := exporterMap[k]
|
||||||
|
if found {
|
||||||
|
log.Debugln("Overriding metric", k, "from user YAML file.")
|
||||||
|
} else {
|
||||||
|
log.Debugln("Adding new metric", k, "from user YAML file.")
|
||||||
|
}
|
||||||
|
exporterMap[k] = v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Merge the query override map
|
||||||
|
for k, v := range newQueryOverrides {
|
||||||
|
_, found := queryOverrideMap[k]
|
||||||
|
if found {
|
||||||
|
log.Debugln("Overriding query override", k, "from user YAML file.")
|
||||||
|
} else {
|
||||||
|
log.Debugln("Adding new query override", k, "from user YAML file.")
|
||||||
|
}
|
||||||
|
queryOverrideMap[k] = v
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// Turn the MetricMap column mapping into a prometheus descriptor mapping.
|
// Turn the MetricMap column mapping into a prometheus descriptor mapping.
|
||||||
func makeDescMap(pgVersion semver.Version, metricMaps map[string]map[string]ColumnMapping) map[string]MetricMapNamespace {
|
func makeDescMap(pgVersion semver.Version, metricMaps map[string]map[string]ColumnMapping) map[string]MetricMapNamespace {
|
||||||
@ -619,6 +682,7 @@ func dbToString(t interface{}) (string, bool) {
|
|||||||
// Exporter collects Postgres metrics. It implements prometheus.Collector.
|
// Exporter collects Postgres metrics. It implements prometheus.Collector.
|
||||||
type Exporter struct {
|
type Exporter struct {
|
||||||
dsn string
|
dsn string
|
||||||
|
userQueriesPath string
|
||||||
duration, error prometheus.Gauge
|
duration, error prometheus.Gauge
|
||||||
totalScrapes prometheus.Counter
|
totalScrapes prometheus.Counter
|
||||||
|
|
||||||
@ -635,9 +699,10 @@ type Exporter struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NewExporter returns a new PostgreSQL exporter for the provided DSN.
|
// NewExporter returns a new PostgreSQL exporter for the provided DSN.
|
||||||
func NewExporter(dsn string) *Exporter {
|
func NewExporter(dsn string, userQueriesPath string) *Exporter {
|
||||||
return &Exporter{
|
return &Exporter{
|
||||||
dsn: dsn,
|
dsn: dsn,
|
||||||
|
userQueriesPath: userQueriesPath,
|
||||||
duration: prometheus.NewGauge(prometheus.GaugeOpts{
|
duration: prometheus.NewGauge(prometheus.GaugeOpts{
|
||||||
Namespace: namespace,
|
Namespace: namespace,
|
||||||
Subsystem: exporter,
|
Subsystem: exporter,
|
||||||
@ -881,6 +946,12 @@ func (e *Exporter) checkMapVersions(ch chan<- prometheus.Metric, db *sql.DB) err
|
|||||||
e.queryOverrides = makeQueryOverrideMap(semanticVersion, queryOverrides)
|
e.queryOverrides = makeQueryOverrideMap(semanticVersion, queryOverrides)
|
||||||
e.lastMapVersion = semanticVersion
|
e.lastMapVersion = semanticVersion
|
||||||
|
|
||||||
|
if e.userQueriesPath != "" {
|
||||||
|
if err := addQueries(e.userQueriesPath, semanticVersion, e.metricMap, e.queryOverrides) ; err != nil {
|
||||||
|
log.Errorln("Failed to reload user queries:", e.userQueriesPath, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
e.mappingMtx.Unlock()
|
e.mappingMtx.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -933,14 +1004,6 @@ func (e *Exporter) scrape(ch chan<- prometheus.Metric) {
|
|||||||
func main() {
|
func main() {
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
// TODO: restroe addQueries functionality
|
|
||||||
//if *queriesPath != "" {
|
|
||||||
// err := addQueries(*queriesPath)
|
|
||||||
// if err != nil {
|
|
||||||
// log.Warnln("Unparseable queries file - discarding merge: ", *queriesPath, err)
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
|
|
||||||
if *onlyDumpMaps {
|
if *onlyDumpMaps {
|
||||||
dumpMaps()
|
dumpMaps()
|
||||||
return
|
return
|
||||||
@ -951,7 +1014,7 @@ func main() {
|
|||||||
log.Fatal("couldn't find environment variable DATA_SOURCE_NAME")
|
log.Fatal("couldn't find environment variable DATA_SOURCE_NAME")
|
||||||
}
|
}
|
||||||
|
|
||||||
exporter := NewExporter(dsn)
|
exporter := NewExporter(dsn, *queriesPath)
|
||||||
prometheus.MustRegister(exporter)
|
prometheus.MustRegister(exporter)
|
||||||
|
|
||||||
http.Handle(*metricPath, prometheus.Handler())
|
http.Handle(*metricPath, prometheus.Handler())
|
||||||
|
Loading…
Reference in New Issue
Block a user