You've already forked postgres_exporter
mirror of
https://github.com/prometheus-community/postgres_exporter.git
synced 2025-08-06 17:22:43 +03:00
Start building out the packages.
This commit is contained in:
@@ -3,12 +3,12 @@ package metricmaps
|
||||
// Metric name parts.
|
||||
const (
|
||||
// Namespace for all metrics.
|
||||
namespace = "pg"
|
||||
ExporterNamespaceLabel = "pg"
|
||||
// Subsystems.
|
||||
exporter = "exporter"
|
||||
ExporterSubsystemLabel = "ExporterSubsystemLabel"
|
||||
// Metric label used for static string data thats handy to send to Prometheus
|
||||
// e.g. version
|
||||
staticLabelName = "static"
|
||||
StaticLabelName = "static"
|
||||
// Metric label used for server identification.
|
||||
serverLabelName = "server"
|
||||
)
|
||||
ServerLabelName = "server"
|
||||
)
|
||||
|
@@ -1,7 +1,17 @@
|
||||
package metricmaps
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/blang/semver"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/common/log"
|
||||
"github.com/wrouesnel/postgres_exporter/pkg/pgdbconv"
|
||||
"math"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Turn the MetricMap column mapping into a prometheus descriptor mapping.
|
||||
func makeDescMap(pgVersion semver.Version, serverLabels prometheus.Labels, metricMaps map[string]intermediateMetricMap) map[string]MetricMapNamespace {
|
||||
func makeDescMap(pgVersion semver.Version, serverLabels prometheus.Labels, metricMaps map[string]IntermediateMetricMap) map[string]MetricMapNamespace {
|
||||
var metricMap = make(map[string]MetricMapNamespace)
|
||||
|
||||
for namespace, intermediateMappings := range metricMaps {
|
||||
@@ -9,23 +19,23 @@ func makeDescMap(pgVersion semver.Version, serverLabels prometheus.Labels, metri
|
||||
|
||||
// Get the constant labels
|
||||
var variableLabels []string
|
||||
for columnName, columnMapping := range intermediateMappings.columnMappings {
|
||||
if columnMapping.usage == queries.LABEL {
|
||||
for columnName, columnMapping := range intermediateMappings.ColumnMappings {
|
||||
if columnMapping.Usage == LABEL {
|
||||
variableLabels = append(variableLabels, columnName)
|
||||
}
|
||||
}
|
||||
|
||||
for columnName, columnMapping := range intermediateMappings.columnMappings {
|
||||
for columnName, columnMapping := range intermediateMappings.ColumnMappings {
|
||||
// Check column version compatibility for the current map
|
||||
// Force to discard if not compatible.
|
||||
if columnMapping.supportedVersions != nil {
|
||||
if !columnMapping.supportedVersions(pgVersion) {
|
||||
if columnMapping.SupportedVersions != nil {
|
||||
if !columnMapping.SupportedVersions.Range(pgVersion) {
|
||||
// It's very useful to be able to see what columns are being
|
||||
// rejected.
|
||||
log.Debugln(columnName, "is being forced to discard due to version incompatibility.")
|
||||
thisMap[columnName] = MetricMap{
|
||||
discard: true,
|
||||
conversion: func(_ interface{}) (float64, bool) {
|
||||
Discard: true,
|
||||
Conversion: func(_ interface{}) (float64, bool) {
|
||||
return math.NaN(), true
|
||||
},
|
||||
}
|
||||
@@ -35,52 +45,52 @@ func makeDescMap(pgVersion semver.Version, serverLabels prometheus.Labels, metri
|
||||
|
||||
// Determine how to convert the column based on its usage.
|
||||
// nolint: dupl
|
||||
switch columnMapping.usage {
|
||||
case queries.DISCARD, queries.LABEL:
|
||||
switch columnMapping.Usage {
|
||||
case DISCARD, LABEL:
|
||||
thisMap[columnName] = MetricMap{
|
||||
discard: true,
|
||||
conversion: func(_ interface{}) (float64, bool) {
|
||||
Discard: true,
|
||||
Conversion: func(_ interface{}) (float64, bool) {
|
||||
return math.NaN(), true
|
||||
},
|
||||
}
|
||||
case queries.COUNTER:
|
||||
case COUNTER:
|
||||
thisMap[columnName] = MetricMap{
|
||||
vtype: prometheus.CounterValue,
|
||||
desc: prometheus.NewDesc(fmt.Sprintf("%s_%s", namespace, columnName), columnMapping.description, variableLabels, serverLabels),
|
||||
conversion: func(in interface{}) (float64, bool) {
|
||||
ValueType: prometheus.CounterValue,
|
||||
Desc: prometheus.NewDesc(fmt.Sprintf("%s_%s", namespace, columnName), columnMapping.Description, variableLabels, serverLabels),
|
||||
Conversion: func(in interface{}) (float64, bool) {
|
||||
return pgdbconv.DBToFloat64(in)
|
||||
},
|
||||
}
|
||||
case queries.GAUGE:
|
||||
case GAUGE:
|
||||
thisMap[columnName] = MetricMap{
|
||||
vtype: prometheus.GaugeValue,
|
||||
desc: prometheus.NewDesc(fmt.Sprintf("%s_%s", namespace, columnName), columnMapping.description, variableLabels, serverLabels),
|
||||
conversion: func(in interface{}) (float64, bool) {
|
||||
ValueType: prometheus.GaugeValue,
|
||||
Desc: prometheus.NewDesc(fmt.Sprintf("%s_%s", namespace, columnName), columnMapping.Description, variableLabels, serverLabels),
|
||||
Conversion: func(in interface{}) (float64, bool) {
|
||||
return pgdbconv.DBToFloat64(in)
|
||||
},
|
||||
}
|
||||
case queries.MAPPEDMETRIC:
|
||||
case MAPPEDMETRIC:
|
||||
thisMap[columnName] = MetricMap{
|
||||
vtype: prometheus.GaugeValue,
|
||||
desc: prometheus.NewDesc(fmt.Sprintf("%s_%s", namespace, columnName), columnMapping.description, variableLabels, serverLabels),
|
||||
conversion: func(in interface{}) (float64, bool) {
|
||||
ValueType: prometheus.GaugeValue,
|
||||
Desc: prometheus.NewDesc(fmt.Sprintf("%s_%s", namespace, columnName), columnMapping.Description, variableLabels, serverLabels),
|
||||
Conversion: func(in interface{}) (float64, bool) {
|
||||
text, ok := in.(string)
|
||||
if !ok {
|
||||
return math.NaN(), false
|
||||
}
|
||||
|
||||
val, ok := columnMapping.mapping[text]
|
||||
val, ok := columnMapping.Mapping[text]
|
||||
if !ok {
|
||||
return math.NaN(), false
|
||||
}
|
||||
return val, true
|
||||
},
|
||||
}
|
||||
case queries.DURATION:
|
||||
case DURATION:
|
||||
thisMap[columnName] = MetricMap{
|
||||
vtype: prometheus.GaugeValue,
|
||||
desc: prometheus.NewDesc(fmt.Sprintf("%s_%s_milliseconds", namespace, columnName), columnMapping.description, variableLabels, serverLabels),
|
||||
conversion: func(in interface{}) (float64, bool) {
|
||||
ValueType: prometheus.GaugeValue,
|
||||
Desc: prometheus.NewDesc(fmt.Sprintf("%s_%s_milliseconds", namespace, columnName), columnMapping.Description, variableLabels, serverLabels),
|
||||
Conversion: func(in interface{}) (float64, bool) {
|
||||
var durationString string
|
||||
switch t := in.(type) {
|
||||
case []byte:
|
||||
@@ -108,11 +118,11 @@ func makeDescMap(pgVersion semver.Version, serverLabels prometheus.Labels, metri
|
||||
}
|
||||
|
||||
metricMap[namespace] = MetricMapNamespace{
|
||||
labels: variableLabels,
|
||||
columnMappings: thisMap,
|
||||
master: intermediateMappings.master,
|
||||
cacheSeconds: intermediateMappings.cacheSeconds}
|
||||
Labels: variableLabels,
|
||||
ColumnMappings: thisMap,
|
||||
Master: intermediateMappings.Master,
|
||||
CacheSeconds: intermediateMappings.CacheSeconds}
|
||||
}
|
||||
|
||||
return metricMap
|
||||
}
|
||||
}
|
||||
|
9
pkg/queries/metricmaps/metricmaps_test.go
Normal file
9
pkg/queries/metricmaps/metricmaps_test.go
Normal file
@@ -0,0 +1,9 @@
|
||||
package metricmaps
|
||||
|
||||
import (
|
||||
. "gopkg.in/check.v1"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// Hook up gocheck into the "go test" runner.
|
||||
func Test(t *testing.T) { TestingT(t) }
|
@@ -5,6 +5,18 @@ import (
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
)
|
||||
|
||||
// QueryMap is the global holder structure for the new unified configuration format.
|
||||
type QueryMap struct {
|
||||
Global *QueryConfig `yaml:"global"`
|
||||
ByServer map[string]*QueryConfig `yaml:"by_server"`
|
||||
}
|
||||
|
||||
// QueryConfig holds a specific mapping and query override config
|
||||
type QueryConfig struct {
|
||||
MetricMap MetricMaps `yaml:"metric_maps"`
|
||||
QueryOverrides QueryOverrides `yaml:"query_override"`
|
||||
}
|
||||
|
||||
// MappingOptions is a copy of ColumnMapping used only for parsing
|
||||
type MappingOptions struct {
|
||||
Usage string `yaml:"usage"`
|
||||
@@ -14,7 +26,7 @@ type MappingOptions struct {
|
||||
}
|
||||
|
||||
// QueryOverrides ensures our query types are consistent
|
||||
type QueryOverrides map[string]string
|
||||
type QueryOverrides map[string][]OverrideQuery
|
||||
|
||||
// MetricMaps is a stub type to assist with checking
|
||||
type MetricMaps map[string]IntermediateMetricMap
|
||||
@@ -27,20 +39,19 @@ type IntermediateMetricMap struct {
|
||||
CacheSeconds uint64
|
||||
}
|
||||
|
||||
|
||||
// MetricMapNamespace groups metric maps under a shared set of labels.
|
||||
type MetricMapNamespace struct {
|
||||
Labels []string // Label names for this namespace
|
||||
ColumnMappings map[string]MetricMap // Column mappings in this namespace
|
||||
Labels []string // Label names for this ExporterNamespaceLabel
|
||||
ColumnMappings map[string]MetricMap // Column mappings in this ExporterNamespaceLabel
|
||||
Master bool // Call query only for master database
|
||||
CacheSeconds uint64 // Number of seconds this metric namespace can be cached. 0 disables.
|
||||
CacheSeconds uint64 // Number of seconds this metric ExporterNamespaceLabel can be cached. 0 disables.
|
||||
}
|
||||
|
||||
// MetricMap stores the prometheus metric description which a given column will
|
||||
// be mapped to by the collector
|
||||
type MetricMap struct {
|
||||
Discard bool // Should metric be discarded during mapping?
|
||||
ValueType prometheus.ValueType // Prometheus valuetype
|
||||
ValueType prometheus.ValueType // Prometheus valuetype
|
||||
Desc *prometheus.Desc // Prometheus descriptor
|
||||
Conversion func(interface{}) (float64, bool) // Conversion function to turn PG result into float64
|
||||
}
|
||||
}
|
||||
|
@@ -7,16 +7,15 @@ import (
|
||||
|
||||
// ColumnUsage should be one of several enum values which describe how a
|
||||
// queried row is to be converted to a Prometheus metric.
|
||||
type ColumnUsage int
|
||||
type ColumnUsage string
|
||||
|
||||
// nolint: golint
|
||||
const (
|
||||
DISCARD ColumnUsage = iota // Ignore this column
|
||||
LABEL ColumnUsage = iota // Use this column as a label
|
||||
COUNTER ColumnUsage = iota // Use this column as a counter
|
||||
GAUGE ColumnUsage = iota // Use this column as a gauge
|
||||
MAPPEDMETRIC ColumnUsage = iota // Use this column with the supplied mapping of text values
|
||||
DURATION ColumnUsage = iota // This column should be interpreted as a text duration (and converted to milliseconds)
|
||||
DISCARD ColumnUsage = "DISCARD" // Ignore this column
|
||||
LABEL ColumnUsage = "LABEL" // Use this column as a label
|
||||
COUNTER ColumnUsage = "COUNTER" // Use this column as a counter
|
||||
GAUGE ColumnUsage = "GAUGE" // Use this column as a gauge
|
||||
MAPPEDMETRIC ColumnUsage = "MAPPEDMETRIC" // Use this column with the supplied mapping of text values
|
||||
DURATION ColumnUsage = "DURATION" // This column should be interpreted as a text duration (and converted to milliseconds)
|
||||
)
|
||||
|
||||
// UnmarshalYAML implements the yaml.Unmarshaller interface.
|
||||
@@ -26,51 +25,29 @@ func (cu *ColumnUsage) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||
return err
|
||||
}
|
||||
|
||||
columnUsage, err := StringToColumnUsage(value)
|
||||
if err != nil {
|
||||
return err
|
||||
var columnUsage ColumnUsage
|
||||
switch value {
|
||||
case "DISCARD",
|
||||
"LABEL",
|
||||
"COUNTER",
|
||||
"GAUGE",
|
||||
"MAPPEDMETRIC",
|
||||
"DURATION":
|
||||
columnUsage = ColumnUsage(value)
|
||||
default:
|
||||
return fmt.Errorf("value is not a valid ColumnUsage value: %s", value)
|
||||
}
|
||||
|
||||
*cu = columnUsage
|
||||
return nil
|
||||
}
|
||||
|
||||
// StringToColumnUsage converts a string to the corresponding ColumnUsage
|
||||
func StringToColumnUsage(s string) (ColumnUsage, error) {
|
||||
var u ColumnUsage
|
||||
var err error
|
||||
switch s {
|
||||
case "DISCARD":
|
||||
u = DISCARD
|
||||
|
||||
case "LABEL":
|
||||
u = LABEL
|
||||
|
||||
case "COUNTER":
|
||||
u = COUNTER
|
||||
|
||||
case "GAUGE":
|
||||
u = GAUGE
|
||||
|
||||
case "MAPPEDMETRIC":
|
||||
u = MAPPEDMETRIC
|
||||
|
||||
case "DURATION":
|
||||
u = DURATION
|
||||
|
||||
default:
|
||||
err = fmt.Errorf("wrong ColumnUsage given : %s", s)
|
||||
}
|
||||
|
||||
return u, err
|
||||
}
|
||||
|
||||
// ColumnMapping is the user-friendly representation of a prometheus descriptor map
|
||||
type ColumnMapping struct {
|
||||
Usage ColumnUsage `yaml:"usage"`
|
||||
Description string `yaml:"description"`
|
||||
Mapping map[string]float64 `yaml:"metric_mapping"` // Optional column mapping for MAPPEDMETRIC
|
||||
SupportedVersions semver.Range `yaml:"pg_version"` // Semantic version ranges which are supported. Unsupported columns are not queried (internally converted to DISCARD).
|
||||
SupportedVersions *SemverRange `yaml:"pg_version"` // Semantic version ranges which are supported. Unsupported columns are not queried (internally converted to DISCARD).
|
||||
}
|
||||
|
||||
// UnmarshalYAML implements yaml.Unmarshaller
|
||||
@@ -79,26 +56,78 @@ func (cm *ColumnMapping) UnmarshalYAML(unmarshal func(interface{}) error) error
|
||||
return unmarshal((*plain)(cm))
|
||||
}
|
||||
|
||||
|
||||
// nolint: golint
|
||||
type Mapping map[string]MappingOptions
|
||||
|
||||
// nolint: golint
|
||||
type UserQuery struct {
|
||||
Query string `yaml:"query"`
|
||||
Metrics []Mapping `yaml:"metrics"`
|
||||
Master bool `yaml:"master"` // Querying only for master database
|
||||
CacheSeconds uint64 `yaml:"cache_seconds"` // Number of seconds to cache the namespace result metrics for.
|
||||
CacheSeconds uint64 `yaml:"cache_seconds"` // Number of seconds to cache the ExporterNamespaceLabel result metrics for.
|
||||
}
|
||||
|
||||
// nolint: golint
|
||||
type UserQueries map[string]UserQuery
|
||||
|
||||
// OverrideQuery are run in-place of simple namespace look ups, and provide
|
||||
// OverrideQuery are run in-place of simple ExporterNamespaceLabel look ups, and provide
|
||||
// advanced functionality. But they have a tendency to postgres version specific.
|
||||
// There aren't too many versions, so we simply store customized versions using
|
||||
// the semver matching we do for columns.
|
||||
type OverrideQuery struct {
|
||||
VersionRange semver.Range
|
||||
Query string
|
||||
}
|
||||
|
||||
// SemverRange implements YAML marshalling for semver.Range
|
||||
type SemverRange struct {
|
||||
r string
|
||||
semver.Range
|
||||
}
|
||||
|
||||
// MustParseSemverRange parses a semver
|
||||
func MustParseSemverRange(s string) *SemverRange {
|
||||
r, err := ParseSemverRange(s)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// ParseSemverRange parses a semver
|
||||
func ParseSemverRange(s string) (*SemverRange, error) {
|
||||
r, err := semver.ParseRange(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &SemverRange{s, r}, nil
|
||||
}
|
||||
|
||||
func (sr *SemverRange) String() string {
|
||||
if sr != nil {
|
||||
return sr.r
|
||||
} else {
|
||||
return "(any)"
|
||||
}
|
||||
}
|
||||
|
||||
// MarshalYAML implements yaml.Marshaller
|
||||
func (sr *SemverRange) MarshalYAML() (interface{}, error) {
|
||||
if sr == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return sr.r, nil
|
||||
}
|
||||
|
||||
// UnmarshalYAML implements yaml.Unmarshaller
|
||||
func (sr *SemverRange) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||
var err error
|
||||
var rangeStr string
|
||||
if err = unmarshal(&rangeStr); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sr, err = ParseSemverRange(rangeStr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
21
pkg/queries/metricmaps/queries_test.go
Normal file
21
pkg/queries/metricmaps/queries_test.go
Normal file
@@ -0,0 +1,21 @@
|
||||
package metricmaps
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
. "gopkg.in/check.v1"
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
type SemverSuite struct{}
|
||||
|
||||
var _ = Suite(&SemverSuite{})
|
||||
|
||||
func (s *SemverSuite) TestEncodeYAML(c *C) {
|
||||
sr, err := ParseSemverRange(">=1.0.0")
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
fmt.Println(sr)
|
||||
b, err := yaml.Marshal(&sr)
|
||||
c.Check(err, IsNil)
|
||||
fmt.Println(string(b))
|
||||
}
|
Reference in New Issue
Block a user