diff --git a/README.md b/README.md index 33c703cde..8a1ea50ce 100644 --- a/README.md +++ b/README.md @@ -42,6 +42,9 @@ Package vendoring is handled with [`govendor`](https://github.com/kardianos/gove * `web.telemetry-path` Path under which to expose metrics. +* `extend.metric-prefix` + Prefix to be added before each metric's name. Empty string by default. + ### Environment Variables The following environment variables configure the exporter: diff --git a/postgres_exporter.go b/postgres_exporter.go index beb690fb3..0425d99af 100644 --- a/postgres_exporter.go +++ b/postgres_exporter.go @@ -35,6 +35,7 @@ var ( listenAddress = kingpin.Flag("web.listen-address", "Address to listen on for web interface and telemetry.").Default(":9187").String() metricPath = kingpin.Flag("web.telemetry-path", "Path under which to expose metrics.").Default("/metrics").String() queriesPath = kingpin.Flag("extend.query-path", "Path to custom queries to run.").Default("").String() + metricPrefix = kingpin.Flag("extend.metric-prefix", "Prefix to be added before metric names.").Default("").String() onlyDumpMaps = kingpin.Flag("dumpmaps", "Do not run, simply dump the maps.").Bool() ) @@ -63,6 +64,14 @@ const ( DURATION ColumnUsage = iota // This column should be interpreted as a text duration (and converted to milliseconds) ) +func prefixMetric(s string) string { + prefix := *metricPrefix + if prefix != "" { + return prefix + s + } + return s +} + // UnmarshalYAML implements the yaml.Unmarshaller interface. func (cu *ColumnUsage) UnmarshalYAML(unmarshal func(interface{}) error) error { var value string @@ -193,15 +202,15 @@ var builtinMetricMaps = map[string]map[string]ColumnMapping{ "count": {GAUGE, "Number of locks", nil, nil}, }, "pg_stat_replication": { - "procpid": {DISCARD, "Process ID of a WAL sender process", nil, semver.MustParseRange("<9.2.0")}, - "pid": {DISCARD, "Process ID of a WAL sender process", nil, semver.MustParseRange(">=9.2.0")}, - "usesysid": {DISCARD, "OID of the user logged into this WAL sender process", nil, nil}, - "usename": {DISCARD, "Name of the user logged into this WAL sender process", nil, nil}, - "application_name": {DISCARD, "Name of the application that is connected to this WAL sender", nil, nil}, - "client_addr": {LABEL, "IP address of the client connected to this WAL sender. If this field is null, it indicates that the client is connected via a Unix socket on the server machine.", nil, nil}, - "client_hostname": {DISCARD, "Host name of the connected client, as reported by a reverse DNS lookup of client_addr. This field will only be non-null for IP connections, and only when log_hostname is enabled.", nil, nil}, - "client_port": {DISCARD, "TCP port number that the client is using for communication with this WAL sender, or -1 if a Unix socket is used", nil, nil}, - "backend_start": {DISCARD, "with time zone Time when this process was started, i.e., when the client connected to this WAL sender", nil, nil}, + "procpid": {DISCARD, "Process ID of a WAL sender process", nil, semver.MustParseRange("<9.2.0")}, + "pid": {DISCARD, "Process ID of a WAL sender process", nil, semver.MustParseRange(">=9.2.0")}, + "usesysid": {DISCARD, "OID of the user logged into cu WAL sender process", nil, nil}, + "usename": {DISCARD, "Name of the user logged into cu WAL sender process", nil, nil}, + "application_name": {DISCARD, "Name of the application that is connected to cu WAL sender", nil, nil}, + "client_addr": {LABEL, "IP address of the client connected to cu WAL sender. If cu field is null, it indicates that the client is connected via a Unix socket on the server machine.", nil, nil}, + "client_hostname": {DISCARD, "Host name of the connected client, as reported by a reverse DNS lookup of client_addr. This field will only be non-null for IP connections, and only when log_hostname is enabled.", nil, nil}, + "client_port": {DISCARD, "TCP port number that the client is using for communication with cu WAL sender, or -1 if a Unix socket is used", nil, nil}, + "backend_start": {DISCARD, "with time zone Time when cu process was started, i.e., when the client connected to cu WAL sender", nil, nil}, "backend_xmin": {DISCARD, "The current backend's xmin horizon.", nil, nil}, "state": {LABEL, "Current WAL sender state", nil, nil}, "sent_location": {DISCARD, "Last transaction log position sent on this connection", nil, semver.MustParseRange("<10.0.0")}, @@ -470,6 +479,7 @@ func makeDescMap(pgVersion semver.Version, metricMaps map[string]map[string]Colu for namespace, mappings := range metricMaps { thisMap := make(map[string]MetricMap) + metricPrefix := prefixMetric(namespace) // Get the constant labels var constLabels []string @@ -509,7 +519,7 @@ func makeDescMap(pgVersion semver.Version, metricMaps map[string]map[string]Colu case COUNTER: thisMap[columnName] = MetricMap{ vtype: prometheus.CounterValue, - desc: prometheus.NewDesc(fmt.Sprintf("%s_%s", namespace, columnName), columnMapping.description, constLabels, nil), + desc: prometheus.NewDesc(fmt.Sprintf("%s_%s", metricPrefix, columnName), columnMapping.description, constLabels, nil), conversion: func(in interface{}) (float64, bool) { return dbToFloat64(in) }, @@ -517,7 +527,7 @@ func makeDescMap(pgVersion semver.Version, metricMaps map[string]map[string]Colu case GAUGE: thisMap[columnName] = MetricMap{ vtype: prometheus.GaugeValue, - desc: prometheus.NewDesc(fmt.Sprintf("%s_%s", namespace, columnName), columnMapping.description, constLabels, nil), + desc: prometheus.NewDesc(fmt.Sprintf("%s_%s", metricPrefix, columnName), columnMapping.description, constLabels, nil), conversion: func(in interface{}) (float64, bool) { return dbToFloat64(in) }, @@ -525,7 +535,7 @@ func makeDescMap(pgVersion semver.Version, metricMaps map[string]map[string]Colu case MAPPEDMETRIC: thisMap[columnName] = MetricMap{ vtype: prometheus.GaugeValue, - desc: prometheus.NewDesc(fmt.Sprintf("%s_%s", namespace, columnName), columnMapping.description, constLabels, nil), + desc: prometheus.NewDesc(fmt.Sprintf("%s_%s", metricPrefix, columnName), columnMapping.description, constLabels, nil), conversion: func(in interface{}) (float64, bool) { text, ok := in.(string) if !ok { @@ -542,7 +552,7 @@ func makeDescMap(pgVersion semver.Version, metricMaps map[string]map[string]Colu case DURATION: thisMap[columnName] = MetricMap{ vtype: prometheus.GaugeValue, - desc: prometheus.NewDesc(fmt.Sprintf("%s_%s_milliseconds", namespace, columnName), columnMapping.description, constLabels, nil), + desc: prometheus.NewDesc(fmt.Sprintf("%s_%s_milliseconds", metricPrefix, columnName), columnMapping.description, constLabels, nil), conversion: func(in interface{}) (float64, bool) { var durationString string switch t := in.(type) { @@ -689,24 +699,25 @@ type Exporter struct { // NewExporter returns a new PostgreSQL exporter for the provided DSN. func NewExporter(dsn string, userQueriesPath string) *Exporter { + metricNamespace := prefixMetric(namespace) return &Exporter{ builtinMetricMaps: builtinMetricMaps, dsn: dsn, userQueriesPath: userQueriesPath, duration: prometheus.NewGauge(prometheus.GaugeOpts{ - Namespace: namespace, + Namespace: metricNamespace, Subsystem: exporter, Name: "last_scrape_duration_seconds", Help: "Duration of the last scrape of metrics from PostgresSQL.", }), totalScrapes: prometheus.NewCounter(prometheus.CounterOpts{ - Namespace: namespace, + Namespace: metricNamespace, Subsystem: exporter, Name: "scrapes_total", Help: "Total number of times PostgresSQL was scraped for metrics.", }), error: prometheus.NewGauge(prometheus.GaugeOpts{ - Namespace: namespace, + Namespace: metricNamespace, Subsystem: exporter, Name: "last_scrape_error", Help: "Whether the last scrape of metrics from PostgreSQL resulted in an error (1 for error, 0 for success).", @@ -768,7 +779,7 @@ func (e *Exporter) Collect(ch chan<- prometheus.Metric) { func newDesc(subsystem, name, help string) *prometheus.Desc { return prometheus.NewDesc( - prometheus.BuildFQName(namespace, subsystem, name), + prometheus.BuildFQName(prefixMetric(namespace), subsystem, name), help, nil, nil, ) } @@ -949,7 +960,7 @@ func (e *Exporter) checkMapVersions(ch chan<- prometheus.Metric, db *sql.DB) err } // Output the version as a special metric - versionDesc := prometheus.NewDesc(fmt.Sprintf("%s_%s", namespace, staticLabelName), + versionDesc := prometheus.NewDesc(fmt.Sprintf("%s_%s", prefixMetric(namespace), staticLabelName), "Version string as reported by postgres", []string{"version", "short_version"}, nil) ch <- prometheus.MustNewConstMetric(versionDesc,