Merge pull request #361 from glightfoot/defaults2

Allow setting defaults for histogram and summary options
This commit is contained in:
Matthias Rampke 2021-02-05 16:32:22 +00:00 committed by GitHub
commit eeeedce405
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 669 additions and 132 deletions

View file

@ -341,9 +341,9 @@ mappings:
error: 0.05 error: 0.05
- quantile: 0.5 - quantile: 0.5
error: 0.005 error: 0.005
max_summary_age: 30s max_age: 30s
summary_age_buckets: 3 age_buckets: 3
stream_buffer_size: 1000 buf_cap: 1000
``` ```
The default quantiles are 0.99, 0.9, and 0.5. The default quantiles are 0.99, 0.9, and 0.5.
@ -414,16 +414,34 @@ mappings:
### Global defaults ### Global defaults
One may also set defaults for the observer type, buckets or quantiles, and match type. One may also set defaults for the observer type, histogram options, summary options, and match type.
These will be used by all mappings that do not define them. These will be used by all mappings that do not define them.
An option that can only be configured in `defaults` is `glob_disable_ordering`, which is `false` if omitted. An option that can only be configured in `defaults` is `glob_disable_ordering`, which is `false` if omitted.
By setting this to `true`, `glob` match type will not honor the occurance of rules in the mapping rules file and always treat `*` as lower priority than a concrete string. By setting this to `true`, `glob` match type will not honor the occurance of rules in the mapping rules file and always treat `*` as lower priority than a concrete string.
Setting `buckets` or `quantiles` in the defaults is deprecated in favor of `histogram_options` and `summary_options`, which will override the deprecated values.
If `summary_options` is present in a mapping config, it will only override the fields set in the mapping. Unset fields in the mapping will take the values from the defaults.
```yaml ```yaml
defaults: defaults:
observer_type: histogram observer_type: histogram
histogram_options:
buckets: [.005, .01, .025, .05, .1, .25, .5, 1, 2.5 ] buckets: [.005, .01, .025, .05, .1, .25, .5, 1, 2.5 ]
summary_options:
quantiles:
- quantile: 0.99
error: 0.001
- quantile: 0.95
error: 0.01
- quantile: 0.9
error: 0.05
- quantile: 0.5
error: 0.005
max_age: 5m
age_buckets: 2
buf_cap: 1000
match_type: glob match_type: glob
glob_disable_ordering: false glob_disable_ordering: false
ttl: 0 # metrics do not expire ttl: 0 # metrics do not expire
@ -435,7 +453,7 @@ mappings:
provider: "$2" provider: "$2"
outcome: "$3" outcome: "$3"
job: "${1}_server" job: "${1}_server"
# This will be a summary timer. # This will be a summary using the summary_options set in `defaults`
- match: "other.distribution.*.*.*" - match: "other.distribution.*.*.*"
observer_type: summary observer_type: summary
name: "other_distribution" name: "other_distribution"

View file

@ -20,6 +20,7 @@ import (
"github.com/go-kit/kit/log" "github.com/go-kit/kit/log"
"github.com/go-kit/kit/log/level" "github.com/go-kit/kit/log/level"
"github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/statsd_exporter/pkg/clock" "github.com/prometheus/statsd_exporter/pkg/clock"
"github.com/prometheus/statsd_exporter/pkg/event" "github.com/prometheus/statsd_exporter/pkg/event"
"github.com/prometheus/statsd_exporter/pkg/mapper" "github.com/prometheus/statsd_exporter/pkg/mapper"

View file

@ -22,8 +22,9 @@ import (
"github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/common/log" "github.com/prometheus/common/log"
"github.com/prometheus/statsd_exporter/pkg/mapper/fsm"
yaml "gopkg.in/yaml.v2" yaml "gopkg.in/yaml.v2"
"github.com/prometheus/statsd_exporter/pkg/mapper/fsm"
) )
var ( var (
@ -77,12 +78,12 @@ func (m *MetricMapper) InitFromYAMLString(fileContents string, cacheSize int, op
return err return err
} }
if n.Defaults.Buckets == nil || len(n.Defaults.Buckets) == 0 { if len(n.Defaults.HistogramOptions.Buckets) == 0 {
n.Defaults.Buckets = prometheus.DefBuckets n.Defaults.HistogramOptions.Buckets = prometheus.DefBuckets
} }
if n.Defaults.Quantiles == nil || len(n.Defaults.Quantiles) == 0 { if len(n.Defaults.SummaryOptions.Quantiles) == 0 {
n.Defaults.Quantiles = defaultQuantiles n.Defaults.SummaryOptions.Quantiles = defaultQuantiles
} }
if n.Defaults.MatchType == MatchTypeDefault { if n.Defaults.MatchType == MatchTypeDefault {
@ -190,7 +191,7 @@ func (m *MetricMapper) InitFromYAMLString(fileContents string, cacheSize int, op
currentMapping.HistogramOptions.Buckets = currentMapping.LegacyBuckets currentMapping.HistogramOptions.Buckets = currentMapping.LegacyBuckets
} }
if currentMapping.HistogramOptions.Buckets == nil || len(currentMapping.HistogramOptions.Buckets) == 0 { if currentMapping.HistogramOptions.Buckets == nil || len(currentMapping.HistogramOptions.Buckets) == 0 {
currentMapping.HistogramOptions.Buckets = n.Defaults.Buckets currentMapping.HistogramOptions.Buckets = n.Defaults.HistogramOptions.Buckets
} }
} }
@ -205,7 +206,16 @@ func (m *MetricMapper) InitFromYAMLString(fileContents string, cacheSize int, op
currentMapping.SummaryOptions.Quantiles = currentMapping.LegacyQuantiles currentMapping.SummaryOptions.Quantiles = currentMapping.LegacyQuantiles
} }
if currentMapping.SummaryOptions.Quantiles == nil || len(currentMapping.SummaryOptions.Quantiles) == 0 { if currentMapping.SummaryOptions.Quantiles == nil || len(currentMapping.SummaryOptions.Quantiles) == 0 {
currentMapping.SummaryOptions.Quantiles = n.Defaults.Quantiles currentMapping.SummaryOptions.Quantiles = n.Defaults.SummaryOptions.Quantiles
}
if currentMapping.SummaryOptions.MaxAge == 0 {
currentMapping.SummaryOptions.MaxAge = n.Defaults.SummaryOptions.MaxAge
}
if currentMapping.SummaryOptions.AgeBuckets == 0 {
currentMapping.SummaryOptions.AgeBuckets = n.Defaults.SummaryOptions.AgeBuckets
}
if currentMapping.SummaryOptions.BufCap == 0 {
currentMapping.SummaryOptions.BufCap = n.Defaults.SummaryOptions.BufCap
} }
} }

View file

@ -17,18 +17,29 @@ import "time"
type mapperConfigDefaults struct { type mapperConfigDefaults struct {
ObserverType ObserverType `yaml:"observer_type"` ObserverType ObserverType `yaml:"observer_type"`
TimerType ObserverType `yaml:"timer_type,omitempty"` // DEPRECATED - field only present to preserve backwards compatibility in configs. Always empty
Buckets []float64 `yaml:"buckets"`
Quantiles []metricObjective `yaml:"quantiles"`
MatchType MatchType `yaml:"match_type"` MatchType MatchType `yaml:"match_type"`
GlobDisableOrdering bool `yaml:"glob_disable_ordering"` GlobDisableOrdering bool `yaml:"glob_disable_ordering"`
Ttl time.Duration `yaml:"ttl"` Ttl time.Duration `yaml:"ttl"`
SummaryOptions SummaryOptions `yaml:"summary_options"`
HistogramOptions HistogramOptions `yaml:"histogram_options"`
}
// mapperConfigDefaultsAlias is used to unmarshal the yaml config into mapperConfigDefaults and allows deprecated fields
type mapperConfigDefaultsAlias struct {
ObserverType ObserverType `yaml:"observer_type"`
TimerType ObserverType `yaml:"timer_type,omitempty"` // DEPRECATED - field only present to preserve backwards compatibility in configs
Buckets []float64 `yaml:"buckets"` // DEPRECATED - field only present to preserve backwards compatibility in configs
Quantiles []metricObjective `yaml:"quantiles"` // DEPRECATED - field only present to preserve backwards compatibility in configs
MatchType MatchType `yaml:"match_type"`
GlobDisableOrdering bool `yaml:"glob_disable_ordering"`
Ttl time.Duration `yaml:"ttl"`
SummaryOptions SummaryOptions `yaml:"summary_options"`
HistogramOptions HistogramOptions `yaml:"histogram_options"`
} }
// UnmarshalYAML is a custom unmarshal function to allow use of deprecated config keys // UnmarshalYAML is a custom unmarshal function to allow use of deprecated config keys
// observer_type will override timer_type // observer_type will override timer_type
func (d *mapperConfigDefaults) UnmarshalYAML(unmarshal func(interface{}) error) error { func (d *mapperConfigDefaults) UnmarshalYAML(unmarshal func(interface{}) error) error {
type mapperConfigDefaultsAlias mapperConfigDefaults
var tmp mapperConfigDefaultsAlias var tmp mapperConfigDefaultsAlias
if err := unmarshal(&tmp); err != nil { if err := unmarshal(&tmp); err != nil {
return err return err
@ -36,16 +47,26 @@ func (d *mapperConfigDefaults) UnmarshalYAML(unmarshal func(interface{}) error)
// Copy defaults // Copy defaults
d.ObserverType = tmp.ObserverType d.ObserverType = tmp.ObserverType
d.Buckets = tmp.Buckets
d.Quantiles = tmp.Quantiles
d.MatchType = tmp.MatchType d.MatchType = tmp.MatchType
d.GlobDisableOrdering = tmp.GlobDisableOrdering d.GlobDisableOrdering = tmp.GlobDisableOrdering
d.Ttl = tmp.Ttl d.Ttl = tmp.Ttl
d.SummaryOptions = tmp.SummaryOptions
d.HistogramOptions = tmp.HistogramOptions
// Use deprecated TimerType if necessary // Use deprecated TimerType if necessary
if tmp.ObserverType == "" { if tmp.ObserverType == "" {
d.ObserverType = tmp.TimerType d.ObserverType = tmp.TimerType
} }
// Use deprecated quantiles if necessary
if len(tmp.SummaryOptions.Quantiles) == 0 && len(tmp.Quantiles) > 0 {
d.SummaryOptions = SummaryOptions{Quantiles: tmp.Quantiles}
}
// Use deprecated buckets if necessary
if len(tmp.HistogramOptions.Buckets) == 0 && len(tmp.Buckets) > 0 {
d.HistogramOptions = HistogramOptions{Buckets: tmp.Buckets}
}
return nil return nil
} }

View file

@ -16,6 +16,8 @@ package mapper
import ( import (
"testing" "testing"
"time" "time"
"github.com/prometheus/client_golang/prometheus"
) )
type mappings []struct { type mappings []struct {
@ -29,18 +31,21 @@ type mappings []struct {
maxAge time.Duration maxAge time.Duration
ageBuckets uint32 ageBuckets uint32
bufCap uint32 bufCap uint32
buckets []float64
} }
func TestMetricMapperYAML(t *testing.T) { func TestMetricMapperYAML(t *testing.T) {
scenarios := []struct { scenarios := []struct {
testName string
config string config string
configBad bool configBad bool
mappings mappings mappings mappings
}{ }{
// Empty config.
{},
// Config with several mapping definitions.
{ {
testName: "Empty config",
},
{
testName: "Config with several mapping definitions",
config: `--- config: `---
mappings: mappings:
- match: test.dispatcher.*.*.* - match: test.dispatcher.*.*.*
@ -153,8 +158,8 @@ mappings:
}, },
}, },
}, },
//Config with backtracking
{ {
testName: "Config with backtracking",
config: ` config: `
defaults: defaults:
glob_disable_ordering: true glob_disable_ordering: true
@ -198,6 +203,7 @@ mappings:
// This test case makes sure the captures in the non-matched later rule // This test case makes sure the captures in the non-matched later rule
// doesn't affect the captures in the first matched rule. // doesn't affect the captures in the first matched rule.
{ {
testName: "Config with backtracking, the non-matched rule has star(s)",
config: ` config: `
defaults: defaults:
glob_disable_ordering: false glob_disable_ordering: false
@ -232,8 +238,8 @@ mappings:
}, },
}, },
}, },
//Config with super sets, disables ordering
{ {
testName: "Config with super sets, disables ordering",
config: ` config: `
defaults: defaults:
glob_disable_ordering: true glob_disable_ordering: true
@ -268,8 +274,8 @@ mappings:
}, },
}, },
}, },
//Config with super sets, keeps ordering
{ {
testName: "Config with super sets, keeps ordering",
config: ` config: `
defaults: defaults:
glob_disable_ordering: false glob_disable_ordering: false
@ -293,8 +299,8 @@ mappings:
}, },
}, },
}, },
// Config with bad regex reference.
{ {
testName: "Config with bad regex reference",
config: `--- config: `---
mappings: mappings:
- match: test.* - match: test.*
@ -312,8 +318,8 @@ mappings:
}, },
}, },
}, },
// Config with good regex reference.
{ {
testName: "Config with good regex reference",
config: ` config: `
mappings: mappings:
- match: test.* - match: test.*
@ -331,8 +337,8 @@ mappings:
}, },
}, },
}, },
// Config with bad metric line.
{ {
testName: "Config with bad metric line",
config: `--- config: `---
mappings: mappings:
- match: bad--metric-line.*.* - match: bad--metric-line.*.*
@ -341,8 +347,8 @@ mappings:
`, `,
configBad: true, configBad: true,
}, },
// Config with dynamic metric name.
{ {
testName: "Config with dynamic metric name",
config: `--- config: `---
mappings: mappings:
- match: test1.*.* - match: test1.*.*
@ -371,8 +377,8 @@ mappings:
}, },
}, },
}, },
// Config with bad metric name.
{ {
testName: "Config with bad metric name",
config: `--- config: `---
mappings: mappings:
- match: test.*.* - match: test.*.*
@ -381,8 +387,8 @@ mappings:
`, `,
configBad: true, configBad: true,
}, },
// Config with no metric name.
{ {
testName: "Config with no metric name",
config: `--- config: `---
mappings: mappings:
- match: test.*.* - match: test.*.*
@ -391,13 +397,13 @@ mappings:
`, `,
configBad: true, configBad: true,
}, },
// Config with no mappings.
{ {
testName: "Config with no mappings",
config: ``, config: ``,
mappings: mappings{}, mappings: mappings{},
}, },
// Config without a trailing newline.
{ {
testName: "Config without a trailing newline",
config: `mappings: config: `mappings:
- match: test.* - match: test.*
name: "name" name: "name"
@ -413,8 +419,8 @@ mappings:
}, },
}, },
}, },
// Config with an improperly escaped *.
{ {
testName: "Config with an improperly escaped *",
config: ` config: `
mappings: mappings:
- match: *.test.* - match: *.test.*
@ -423,8 +429,8 @@ mappings:
label: "${1}_foo"`, label: "${1}_foo"`,
configBad: true, configBad: true,
}, },
// Config with a properly escaped *.
{ {
testName: "Config with a properly escaped *",
config: ` config: `
mappings: mappings:
- match: "*.test.*" - match: "*.test.*"
@ -441,8 +447,8 @@ mappings:
}, },
}, },
}, },
// Config with good observer type.
{ {
testName: "Config with good observer type",
config: `--- config: `---
mappings: mappings:
- match: test.*.* - match: test.*.*
@ -467,8 +473,8 @@ mappings:
}, },
}, },
}, },
// Config with good observer type and unused timer type
{ {
testName: "Config with good observer type and unused timer type",
config: `--- config: `---
mappings: mappings:
- match: test.*.* - match: test.*.*
@ -495,6 +501,7 @@ mappings:
}, },
}, },
{ {
testName: "Config with good observertype and no defaults",
config: `--- config: `---
mappings: mappings:
- match: test1.*.* - match: test1.*.*
@ -515,8 +522,8 @@ mappings:
}, },
}, },
}, },
// Config with good deprecated timer type
{ {
testName: "Config with good deprecated timer type",
config: `--- config: `---
mappings: mappings:
- match: test1.*.* - match: test1.*.*
@ -537,8 +544,8 @@ mappings:
}, },
}, },
}, },
// Config with bad observer type.
{ {
testName: "Config with bad observer type",
config: `--- config: `---
mappings: mappings:
- match: test.*.* - match: test.*.*
@ -548,8 +555,8 @@ mappings:
`, `,
configBad: true, configBad: true,
}, },
// Config with bad deprecated timer type.
{ {
testName: "Config with bad deprecated timer type",
config: `--- config: `---
mappings: mappings:
- match: test.*.* - match: test.*.*
@ -559,8 +566,8 @@ mappings:
`, `,
configBad: true, configBad: true,
}, },
// new style quantiles
{ {
testName: "New style quantiles",
config: `--- config: `---
mappings: mappings:
- match: test.*.* - match: test.*.*
@ -586,8 +593,8 @@ mappings:
}, },
}, },
}, },
// Config with summary configuration.
{ {
testName: "Config with summary options",
config: `--- config: `---
mappings: mappings:
- match: test.*.* - match: test.*.*
@ -619,8 +626,457 @@ mappings:
}, },
}, },
}, },
// duplicate quantiles are bad
{ {
testName: "Config with default summary options",
config: `---
defaults:
summary_options:
quantiles:
- quantile: 0.42
error: 0.04
- quantile: 0.7
error: 0.002
max_age: 5m
age_buckets: 2
buf_cap: 1000
mappings:
- match: test.*.*
observer_type: summary
name: "foo"
labels: {}
`,
mappings: mappings{
{
statsdMetric: "test.*.*",
name: "foo",
labels: map[string]string{},
quantiles: []metricObjective{
{Quantile: 0.42, Error: 0.04},
{Quantile: 0.7, Error: 0.002},
},
maxAge: 5 * time.Minute,
ageBuckets: 2,
bufCap: 1000,
},
},
},
{
testName: "Config with default summary options without quantiles",
config: `---
defaults:
summary_options:
max_age: 5m
age_buckets: 2
buf_cap: 1000
mappings:
- match: test.*.*
observer_type: summary
name: "foo"
labels: {}
`,
mappings: mappings{
{
statsdMetric: "test.*.*",
name: "foo",
labels: map[string]string{},
quantiles: defaultQuantiles,
maxAge: 5 * time.Minute,
ageBuckets: 2,
bufCap: 1000,
},
},
},
{
testName: "Config with default summary options overrides quantiles",
config: `---
defaults:
quantiles:
- quantile: 0.9
error: 0.1
- quantile: 0.99
error: 0.01
summary_options:
quantiles:
- quantile: 0.42
error: 0.04
- quantile: 0.7
error: 0.002
max_age: 5m
age_buckets: 2
buf_cap: 1000
mappings:
- match: test.*.*
observer_type: summary
name: "foo"
labels: {}
`,
mappings: mappings{
{
statsdMetric: "test.*.*",
name: "foo",
labels: map[string]string{},
quantiles: []metricObjective{
{Quantile: 0.42, Error: 0.04},
{Quantile: 0.7, Error: 0.002},
},
maxAge: 5 * time.Minute,
ageBuckets: 2,
bufCap: 1000,
},
},
},
{
testName: "Config that overrides default summary options",
config: `---
defaults:
summary_options:
quantiles:
- quantile: 0.042
error: 0.4
- quantile: 0.07
error: 0.02
max_age: 15m
age_buckets: 3
buf_cap: 100
mappings:
- match: test.*.*
observer_type: summary
name: "foo"
labels: {}
summary_options:
quantiles:
- quantile: 0.42
error: 0.04
- quantile: 0.7
error: 0.002
max_age: 5m
age_buckets: 2
buf_cap: 1000
`,
mappings: mappings{
{
statsdMetric: "test.*.*",
name: "foo",
labels: map[string]string{},
quantiles: []metricObjective{
{Quantile: 0.42, Error: 0.04},
{Quantile: 0.7, Error: 0.002},
},
maxAge: 5 * time.Minute,
ageBuckets: 2,
bufCap: 1000,
},
},
},
{
testName: "Config that overrides default summary options and a default options mapping",
config: `---
defaults:
summary_options:
quantiles:
- quantile: 0.9
error: 0.1
- quantile: 0.99
error: 0.01
max_age: 15m
age_buckets: 3
buf_cap: 100
mappings:
- match: test.*.*
observer_type: summary
name: "foo"
labels: {}
summary_options:
quantiles:
- quantile: 0.42
error: 0.04
- quantile: 0.7
error: 0.002
max_age: 5m
age_buckets: 2
buf_cap: 1000
- match: test_default.*.*
observer_type: summary
name: "foo_default"
labels: {}
`,
mappings: mappings{
{
statsdMetric: "test.*.*",
name: "foo",
labels: map[string]string{},
quantiles: []metricObjective{
{Quantile: 0.42, Error: 0.04},
{Quantile: 0.7, Error: 0.002},
},
maxAge: 5 * time.Minute,
ageBuckets: 2,
bufCap: 1000,
},
{
statsdMetric: "test_default.*.*",
name: "foo_default",
labels: map[string]string{},
quantiles: []metricObjective{
{Quantile: 0.9, Error: 0.1},
{Quantile: 0.99, Error: 0.01},
},
maxAge: 15 * time.Minute,
ageBuckets: 3,
bufCap: 100,
},
},
},
{
testName: "Config that partially overrides default summary quantiles and a default options mapping",
config: `---
defaults:
summary_options:
quantiles:
- quantile: 0.9
error: 0.1
- quantile: 0.99
error: 0.01
max_age: 15m
age_buckets: 3
buf_cap: 100
mappings:
- match: test.*.*
observer_type: summary
name: "foo"
labels: {}
summary_options:
quantiles:
- quantile: 0.42
error: 0.04
- quantile: 0.7
error: 0.002
- match: test_default.*.*
observer_type: summary
name: "foo_default"
labels: {}
`,
mappings: mappings{
{
statsdMetric: "test.*.*",
name: "foo",
labels: map[string]string{},
quantiles: []metricObjective{
{Quantile: 0.42, Error: 0.04},
{Quantile: 0.7, Error: 0.002},
},
maxAge: 15 * time.Minute,
ageBuckets: 3,
bufCap: 100,
},
{
statsdMetric: "test_default.*.*",
name: "foo_default",
labels: map[string]string{},
quantiles: []metricObjective{
{Quantile: 0.9, Error: 0.1},
{Quantile: 0.99, Error: 0.01},
},
maxAge: 15 * time.Minute,
ageBuckets: 3,
bufCap: 100,
},
},
},
{
testName: "Config that partially overrides default summary max_age and a default options mapping",
config: `---
defaults:
summary_options:
quantiles:
- quantile: 0.9
error: 0.1
- quantile: 0.99
error: 0.01
max_age: 15m
age_buckets: 3
buf_cap: 100
mappings:
- match: test.*.*
observer_type: summary
name: "foo"
labels: {}
summary_options:
max_age: 5m
- match: test_default.*.*
observer_type: summary
name: "foo_default"
labels: {}
`,
mappings: mappings{
{
statsdMetric: "test.*.*",
name: "foo",
labels: map[string]string{},
quantiles: []metricObjective{
{Quantile: 0.9, Error: 0.1},
{Quantile: 0.99, Error: 0.01},
},
maxAge: 5 * time.Minute,
ageBuckets: 3,
bufCap: 100,
},
{
statsdMetric: "test_default.*.*",
name: "foo_default",
labels: map[string]string{},
quantiles: []metricObjective{
{Quantile: 0.9, Error: 0.1},
{Quantile: 0.99, Error: 0.01},
},
maxAge: 15 * time.Minute,
ageBuckets: 3,
bufCap: 100,
},
},
},
{
testName: "Config with histogram options",
config: `---
mappings:
- match: test.*.*
observer_type: histogram
name: "foo"
labels: {}
histogram_options:
buckets: [0.1, 1, 10, 100, 1000]
`,
mappings: mappings{
{
statsdMetric: "test.*.*",
name: "foo",
labels: map[string]string{},
buckets: []float64{0.1, 1, 10, 100, 1000},
},
},
},
{
testName: "Config with default histogram options",
config: `---
defaults:
histogram_options:
buckets: [0.1, 1, 10, 100, 1000]
mappings:
- match: test.*.*
observer_type: histogram
name: "foo"
labels: {}
`,
mappings: mappings{
{
statsdMetric: "test.*.*",
name: "foo",
labels: map[string]string{},
buckets: []float64{0.1, 1, 10, 100, 1000},
},
},
},
{
testName: "Config with default histogram options without buckets",
config: `---
defaults:
histogram_options:
buckets: []
mappings:
- match: test.*.*
observer_type: histogram
name: "foo"
labels: {}
`,
mappings: mappings{
{
statsdMetric: "test.*.*",
name: "foo",
labels: map[string]string{},
buckets: prometheus.DefBuckets,
},
},
},
{
testName: "Config with default histogram options overrides buckets",
config: `---
defaults:
buckets: [0.2, 2, 20, 200, 2000]
histogram_options:
buckets: [0.1, 1, 10, 100, 1000]
mappings:
- match: test.*.*
observer_type: histogram
name: "foo"
labels: {}
`,
mappings: mappings{
{
statsdMetric: "test.*.*",
name: "foo",
labels: map[string]string{},
buckets: []float64{0.1, 1, 10, 100, 1000},
},
},
},
{
testName: "Config that overrides default histogram configuration",
config: `---
defaults:
histogram_options:
buckets: [0.2, 2, 20, 200, 2000]
mappings:
- match: test.*.*
observer_type: histogram
name: "foo"
labels: {}
histogram_options:
buckets: [0.1, 1, 10, 100, 1000]
`,
mappings: mappings{
{
statsdMetric: "test.*.*",
name: "foo",
labels: map[string]string{},
buckets: []float64{0.1, 1, 10, 100, 1000},
},
},
},
{
testName: "Config that overrides default histogram configuration and a default options mapping",
config: `---
defaults:
histogram_options:
buckets: [0.2, 2, 20, 200]
mappings:
- match: test.*.*
observer_type: histogram
name: "foo"
labels: {}
histogram_options:
buckets: [0.1, 1, 10, 100, 1000]
- match: test_default.*.*
observer_type: histogram
name: "foo_default"
labels: {}
`,
mappings: mappings{
{
statsdMetric: "test.*.*",
name: "foo",
labels: map[string]string{},
buckets: []float64{0.1, 1, 10, 100, 1000},
},
{
statsdMetric: "test_default.*.*",
name: "foo_default",
labels: map[string]string{},
buckets: []float64{0.2, 2, 20, 200},
},
},
},
{
testName: "Duplicate quantiles are bad",
config: `--- config: `---
mappings: mappings:
- match: test.*.* - match: test.*.*
@ -637,8 +1093,8 @@ mappings:
`, `,
configBad: true, configBad: true,
}, },
// Config with good metric type.
{ {
testName: "Config with good metric type",
config: `--- config: `---
mappings: mappings:
- match: test.*.* - match: test.*.*
@ -647,8 +1103,8 @@ mappings:
labels: {} labels: {}
`, `,
}, },
// Config with good metric type observer.
{ {
testName: "Config with good metric type observer",
config: `--- config: `---
mappings: mappings:
- match: test.*.* - match: test.*.*
@ -657,8 +1113,8 @@ mappings:
labels: {} labels: {}
`, `,
}, },
// Config with good metric type timer.
{ {
testName: "Config with good metric type timer",
config: `--- config: `---
mappings: mappings:
- match: test.*.* - match: test.*.*
@ -667,8 +1123,8 @@ mappings:
labels: {} labels: {}
`, `,
}, },
// Config with bad metric type matcher.
{ {
testName: "Config with bad metric type matcher",
config: `--- config: `---
mappings: mappings:
- match: test.*.* - match: test.*.*
@ -678,8 +1134,8 @@ mappings:
`, `,
configBad: true, configBad: true,
}, },
// Config with multiple explicit metric types
{ {
testName: "Config with multiple explicit metric types",
config: `--- config: `---
mappings: mappings:
- match: test.foo.* - match: test.foo.*
@ -702,8 +1158,8 @@ mappings:
}, },
}, },
}, },
//Config with uncompilable regex.
{ {
testName: "Config with uncompilable regex",
config: `--- config: `---
mappings: mappings:
- match: "*\\.foo" - match: "*\\.foo"
@ -713,8 +1169,8 @@ mappings:
`, `,
configBad: true, configBad: true,
}, },
//Config with non-matched metric.
{ {
testName: "Config with non-matched metric",
config: `--- config: `---
mappings: mappings:
- match: foo.*.* - match: foo.*.*
@ -731,8 +1187,8 @@ mappings:
}, },
}, },
}, },
//Config with no name.
{ {
testName: "Config with no name",
config: `--- config: `---
mappings: mappings:
- match: *\.foo - match: *\.foo
@ -743,6 +1199,7 @@ mappings:
configBad: true, configBad: true,
}, },
{ {
testName: "Config with labels from glob",
config: `--- config: `---
mappings: mappings:
- match: p.*.*.c.* - match: p.*.*.c.*
@ -765,8 +1222,8 @@ mappings:
}, },
}, },
}, },
// Example from the README.
{ {
testName: "Example from the README",
config: ` config: `
mappings: mappings:
- match: test.dispatcher.*.*.* - match: test.dispatcher.*.*.*
@ -810,8 +1267,8 @@ mappings:
}, },
}, },
}, },
// Config that drops all.
{ {
testName: "Config that drops all",
config: `mappings: config: `mappings:
- match: . - match: .
match_type: regex match_type: regex
@ -826,8 +1283,8 @@ mappings:
}, },
}, },
}, },
// Config that has a catch-all to drop all.
{ {
testName: "Config that has a catch-all to drop all",
config: `mappings: config: `mappings:
- match: web.* - match: web.*
name: "web" name: "web"
@ -850,8 +1307,8 @@ mappings:
}, },
}, },
}, },
// Config that has a ttl.
{ {
testName: "Config that has a ttl",
config: `mappings: config: `mappings:
- match: web.* - match: web.*
name: "web" name: "web"
@ -872,8 +1329,8 @@ mappings:
}, },
}, },
}, },
// Config that has a default ttl.
{ {
testName: "Config that has a default ttl",
config: `defaults: config: `defaults:
ttl: 1m2s ttl: 1m2s
mappings: mappings:
@ -895,8 +1352,8 @@ mappings:
}, },
}, },
}, },
// Config that override a default ttl.
{ {
testName: "Config that override a default ttl",
config: `defaults: config: `defaults:
ttl: 1m2s ttl: 1m2s
mappings: mappings:
@ -923,6 +1380,10 @@ mappings:
mapper := MetricMapper{} mapper := MetricMapper{}
for i, scenario := range scenarios { for i, scenario := range scenarios {
if scenario.testName == "" {
t.Fatalf("Missing testName in scenario %+v", scenario)
}
t.Run(scenario.testName, func(t *testing.T) {
err := mapper.InitFromYAMLString(scenario.config, 1000) err := mapper.InitFromYAMLString(scenario.config, 1000)
if err != nil && !scenario.configBad { if err != nil && !scenario.configBad {
t.Fatalf("%d. Config load error: %s %s", i, scenario.config, err) t.Fatalf("%d. Config load error: %s %s", i, scenario.config, err)
@ -960,6 +1421,17 @@ mappings:
t.Fatalf("%d.%q: Expected match metric of %s, got %s", i, metric, mapType, m.MatchMetricType) t.Fatalf("%d.%q: Expected match metric of %s, got %s", i, metric, mapType, m.MatchMetricType)
} }
if len(mapping.buckets) != 0 {
if len(mapping.buckets) != len(m.HistogramOptions.Buckets) {
t.Fatalf("%d.%q: Expected %d buckets, got %d", i, metric, len(mapping.buckets), len(m.HistogramOptions.Buckets))
}
for i, bucket := range mapping.buckets {
if bucket != m.HistogramOptions.Buckets[i] {
t.Fatalf("%d.%q: Expected bucket %v, got %v", i, metric, m.HistogramOptions.Buckets[i], bucket)
}
}
}
if len(mapping.quantiles) != 0 { if len(mapping.quantiles) != 0 {
if len(mapping.quantiles) != len(m.SummaryOptions.Quantiles) { if len(mapping.quantiles) != len(m.SummaryOptions.Quantiles) {
t.Fatalf("%d.%q: Expected %d quantiles, got %d", i, metric, len(mapping.quantiles), len(m.SummaryOptions.Quantiles)) t.Fatalf("%d.%q: Expected %d quantiles, got %d", i, metric, len(mapping.quantiles), len(m.SummaryOptions.Quantiles))
@ -983,17 +1455,19 @@ mappings:
t.Fatalf("%d.%q: Expected max age %v, got %v", i, metric, mapping.bufCap, m.SummaryOptions.BufCap) t.Fatalf("%d.%q: Expected max age %v, got %v", i, metric, mapping.bufCap, m.SummaryOptions.BufCap)
} }
} }
})
} }
} }
func TestAction(t *testing.T) { func TestAction(t *testing.T) {
scenarios := []struct { scenarios := []struct {
testName string
config string config string
configBad bool configBad bool
expectedAction ActionType expectedAction ActionType
}{ }{
{ {
// no action set testName: "no action set",
config: `--- config: `---
mappings: mappings:
- match: test.*.* - match: test.*.*
@ -1003,7 +1477,7 @@ mappings:
expectedAction: ActionTypeMap, expectedAction: ActionTypeMap,
}, },
{ {
// map action set testName: "map action set",
config: `--- config: `---
mappings: mappings:
- match: test.*.* - match: test.*.*
@ -1014,7 +1488,7 @@ mappings:
expectedAction: ActionTypeMap, expectedAction: ActionTypeMap,
}, },
{ {
// drop action set testName: "drop action set",
config: `--- config: `---
mappings: mappings:
- match: test.*.* - match: test.*.*
@ -1025,7 +1499,7 @@ mappings:
expectedAction: ActionTypeDrop, expectedAction: ActionTypeDrop,
}, },
{ {
// invalid action set testName: "invalid action set",
config: `--- config: `---
mappings: mappings:
- match: test.*.* - match: test.*.*
@ -1036,7 +1510,7 @@ mappings:
expectedAction: ActionTypeDrop, expectedAction: ActionTypeDrop,
}, },
{ {
// valid yaml example testName: "valid yaml example",
config: `--- config: `---
mappings: mappings:
- match: "test\\.(\\w+)\\.(\\w+)\\.counter" - match: "test\\.(\\w+)\\.(\\w+)\\.counter"
@ -1049,7 +1523,7 @@ mappings:
expectedAction: ActionTypeMap, expectedAction: ActionTypeMap,
}, },
{ {
// invalid yaml example testName: "invalid yaml example",
config: `--- config: `---
mappings: mappings:
- match: "test\.(\w+)\.(\w+)\.counter" - match: "test\.(\w+)\.(\w+)\.counter"
@ -1063,6 +1537,10 @@ mappings:
} }
for i, scenario := range scenarios { for i, scenario := range scenarios {
if scenario.testName == "" {
t.Fatalf("Missing testName in scenario %+v", scenario)
}
t.Run(scenario.testName, func(t *testing.T) {
mapper := MetricMapper{} mapper := MetricMapper{}
err := mapper.InitFromYAMLString(scenario.config, 0) err := mapper.InitFromYAMLString(scenario.config, 0)
if err != nil && !scenario.configBad { if err != nil && !scenario.configBad {
@ -1078,6 +1556,7 @@ mappings:
t.Fatalf("%d: Expected action %v, got %v", i, scenario.expectedAction, a) t.Fatalf("%d: Expected action %v, got %v", i, scenario.expectedAction, a)
} }
} }
})
} }
} }

View file

@ -23,6 +23,7 @@ import (
"github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/common/model" "github.com/prometheus/common/model"
"github.com/prometheus/statsd_exporter/pkg/clock" "github.com/prometheus/statsd_exporter/pkg/clock"
"github.com/prometheus/statsd_exporter/pkg/mapper" "github.com/prometheus/statsd_exporter/pkg/mapper"
"github.com/prometheus/statsd_exporter/pkg/metrics" "github.com/prometheus/statsd_exporter/pkg/metrics"
@ -248,7 +249,7 @@ func (r *Registry) GetHistogram(metricName string, labels prometheus.Labels, hel
var histogramVec *prometheus.HistogramVec var histogramVec *prometheus.HistogramVec
if vh == nil { if vh == nil {
metricsCount.WithLabelValues("histogram").Inc() metricsCount.WithLabelValues("histogram").Inc()
buckets := r.Mapper.Defaults.Buckets buckets := r.Mapper.Defaults.HistogramOptions.Buckets
if mapping.HistogramOptions != nil && len(mapping.HistogramOptions.Buckets) > 0 { if mapping.HistogramOptions != nil && len(mapping.HistogramOptions.Buckets) > 0 {
buckets = mapping.HistogramOptions.Buckets buckets = mapping.HistogramOptions.Buckets
} }
@ -295,14 +296,21 @@ func (r *Registry) GetSummary(metricName string, labels prometheus.Labels, help
var summaryVec *prometheus.SummaryVec var summaryVec *prometheus.SummaryVec
if vh == nil { if vh == nil {
metricsCount.WithLabelValues("summary").Inc() metricsCount.WithLabelValues("summary").Inc()
quantiles := r.Mapper.Defaults.Quantiles quantiles := r.Mapper.Defaults.SummaryOptions.Quantiles
if mapping != nil && mapping.SummaryOptions != nil && len(mapping.SummaryOptions.Quantiles) > 0 { if mapping != nil && mapping.SummaryOptions != nil && len(mapping.SummaryOptions.Quantiles) > 0 {
quantiles = mapping.SummaryOptions.Quantiles quantiles = mapping.SummaryOptions.Quantiles
} }
summaryOptions := mapper.SummaryOptions{}
summaryOptions := mapper.SummaryOptions{
MaxAge: r.Mapper.Defaults.SummaryOptions.MaxAge,
AgeBuckets: r.Mapper.Defaults.SummaryOptions.AgeBuckets,
BufCap: r.Mapper.Defaults.SummaryOptions.BufCap,
}
if mapping != nil && mapping.SummaryOptions != nil { if mapping != nil && mapping.SummaryOptions != nil {
summaryOptions = *mapping.SummaryOptions summaryOptions = *mapping.SummaryOptions
} }
objectives := make(map[float64]float64) objectives := make(map[float64]float64)
for _, q := range quantiles { for _, q := range quantiles {
objectives[q.Quantile] = q.Error objectives[q.Quantile] = q.Error