create sub-hierarchies for summary and histogram options

Currently the Buckets and Quantiles settings are top level settings per metric
in the yaml.  In a subsequent commit we're going to allow adding more such
options for summaries, at which point having them all at the top level gets
confusing.

Split the options out into separate hierarchies to allow adding more options,
without adding confusion.  We preserve backwards compatibility by still
accepting the old option, but warning when it is present.

Signed-off-by: Thomas Gummerer <t.gummerer@gmail.com>
This commit is contained in:
Thomas Gummerer 2020-01-16 16:55:09 +00:00
parent c9c23a4f9e
commit 80b77513a6
4 changed files with 148 additions and 39 deletions

View file

@ -247,15 +247,16 @@ mappings:
provider: "$2" provider: "$2"
outcome: "$3" outcome: "$3"
job: "${1}_server" job: "${1}_server"
quantiles: summary_options:
- quantile: 0.99 quantiles:
error: 0.001 - quantile: 0.99
- quantile: 0.95 error: 0.001
error: 0.01 - quantile: 0.95
- quantile: 0.9 error: 0.01
error: 0.05 - quantile: 0.9
- quantile: 0.5 error: 0.05
error: 0.005 - quantile: 0.5
error: 0.005
``` ```
The default quantiles are 0.99, 0.9, and 0.5. The default quantiles are 0.99, 0.9, and 0.5.
@ -268,7 +269,8 @@ to set the timer type for a single metric:
mappings: mappings:
- match: "test.timing.*.*.*" - match: "test.timing.*.*.*"
timer_type: histogram timer_type: histogram
buckets: [ 0.01, 0.025, 0.05, 0.1 ] histogram_options:
buckets: [ 0.01, 0.025, 0.05, 0.1 ]
name: "my_timer" name: "my_timer"
labels: labels:
provider: "$2" provider: "$2"

View file

@ -57,21 +57,31 @@ type MetricMapper struct {
} }
type MetricMapping struct { type MetricMapping struct {
Match string `yaml:"match"` Match string `yaml:"match"`
Name string `yaml:"name"` Name string `yaml:"name"`
nameFormatter *fsm.TemplateFormatter nameFormatter *fsm.TemplateFormatter
regex *regexp.Regexp regex *regexp.Regexp
Labels prometheus.Labels `yaml:"labels"` Labels prometheus.Labels `yaml:"labels"`
labelKeys []string labelKeys []string
labelFormatters []*fsm.TemplateFormatter labelFormatters []*fsm.TemplateFormatter
TimerType TimerType `yaml:"timer_type"` TimerType TimerType `yaml:"timer_type"`
Buckets []float64 `yaml:"buckets"` LegacyBuckets []float64 `yaml:"buckets"`
Quantiles []metricObjective `yaml:"quantiles"` LegacyQuantiles []metricObjective `yaml:"quantiles"`
MatchType MatchType `yaml:"match_type"` MatchType MatchType `yaml:"match_type"`
HelpText string `yaml:"help"` HelpText string `yaml:"help"`
Action ActionType `yaml:"action"` Action ActionType `yaml:"action"`
MatchMetricType MetricType `yaml:"match_metric_type"` MatchMetricType MetricType `yaml:"match_metric_type"`
Ttl time.Duration `yaml:"ttl"` Ttl time.Duration `yaml:"ttl"`
SummaryOptions *SummaryOptions `yaml:"summary_options"`
HistogramOptions *HistogramOptions `yaml:"histogram_options"`
}
type SummaryOptions struct {
Quantiles []metricObjective `yaml:"quantiles"`
}
type HistogramOptions struct {
Buckets []float64 `yaml:"buckets"`
} }
type metricObjective struct { type metricObjective struct {
@ -172,12 +182,60 @@ func (m *MetricMapper) InitFromYAMLString(fileContents string, cacheSize int) er
currentMapping.TimerType = n.Defaults.TimerType currentMapping.TimerType = n.Defaults.TimerType
} }
if currentMapping.Buckets == nil || len(currentMapping.Buckets) == 0 { if currentMapping.LegacyQuantiles != nil &&
currentMapping.Buckets = n.Defaults.Buckets (currentMapping.SummaryOptions == nil || currentMapping.SummaryOptions.Quantiles != nil) {
log.Warn("using the top level quantiles is deprecated. Please use quantiles in the summary_options hierarchy")
} }
if currentMapping.Quantiles == nil || len(currentMapping.Quantiles) == 0 { if currentMapping.LegacyBuckets != nil &&
currentMapping.Quantiles = n.Defaults.Quantiles (currentMapping.HistogramOptions == nil || currentMapping.HistogramOptions.Buckets != nil) {
log.Warn("using the top level buckets is deprecated. Please use buckets in the histogram_options hierarchy")
}
if currentMapping.SummaryOptions != nil &&
currentMapping.LegacyQuantiles != nil &&
currentMapping.SummaryOptions.Quantiles != nil {
return fmt.Errorf("cannot use quantiles in both the top level and summary options at the same time in %s", currentMapping.Match)
}
if currentMapping.HistogramOptions != nil &&
currentMapping.LegacyBuckets != nil &&
currentMapping.HistogramOptions.Buckets != nil {
return fmt.Errorf("cannot use buckets in both the top level and histogram options at the same time in %s", currentMapping.Match)
}
if currentMapping.TimerType == TimerTypeHistogram {
if currentMapping.SummaryOptions != nil {
return fmt.Errorf("cannot use histogram timer and summary options at the same time")
}
if currentMapping.HistogramOptions == nil {
currentMapping.HistogramOptions = &HistogramOptions{}
}
if currentMapping.LegacyBuckets != nil && len(currentMapping.LegacyBuckets) != 0 {
currentMapping.HistogramOptions.Buckets = currentMapping.LegacyBuckets
}
if currentMapping.HistogramOptions.Buckets == nil || len(currentMapping.HistogramOptions.Buckets) == 0 {
currentMapping.HistogramOptions.Buckets = n.Defaults.Buckets
}
}
if currentMapping.TimerType == TimerTypeSummary {
if currentMapping.HistogramOptions != nil {
return fmt.Errorf("cannot use summary timer and histogram options at the same time")
}
if currentMapping.SummaryOptions == nil {
currentMapping.SummaryOptions = &SummaryOptions{}
}
}
if currentMapping.LegacyQuantiles == nil || len(currentMapping.LegacyQuantiles) == 0 {
currentMapping.LegacyQuantiles = n.Defaults.Quantiles
if currentMapping.LegacyQuantiles != nil && len(currentMapping.LegacyQuantiles) != 0 {
currentMapping.SummaryOptions.Quantiles = currentMapping.LegacyQuantiles
}
if currentMapping.SummaryOptions.Quantiles == nil || len(currentMapping.SummaryOptions.Quantiles) == 0 {
currentMapping.SummaryOptions.Quantiles = n.Defaults.Quantiles
}
} }
if currentMapping.Ttl == 0 && n.Defaults.Ttl > 0 { if currentMapping.Ttl == 0 && n.Defaults.Ttl > 0 {

View file

@ -496,6 +496,51 @@ mappings:
`, `,
configBad: true, configBad: true,
}, },
// new style quantiles
{
config: `---
mappings:
- match: test.*.*
timer_type: summary
name: "foo"
labels: {}
summary_options:
quantiles:
- quantile: 0.42
error: 0.04
- quantile: 0.7
error: 0.002
`,
mappings: mappings{
{
statsdMetric: "test.*.*",
name: "foo",
labels: map[string]string{},
quantiles: []metricObjective{
{Quantile: 0.42, Error: 0.04},
{Quantile: 0.7, Error: 0.002},
},
},
},
},
// duplicate quantiles are bad
{
config: `---
mappings:
- match: test.*.*
timer_type: summary
name: "foo"
labels: {}
quantiles:
- quantile: 0.42
error: 0.04
summary_options:
quantiles:
- quantile: 0.42
error: 0.04
`,
configBad: true,
},
// Config with good metric type. // Config with good metric type.
{ {
config: `--- config: `---
@ -777,15 +822,15 @@ mappings:
} }
if len(mapping.quantiles) != 0 { if len(mapping.quantiles) != 0 {
if len(mapping.quantiles) != len(m.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.Quantiles)) t.Fatalf("%d.%q: Expected %d quantiles, got %d", i, metric, len(mapping.quantiles), len(m.SummaryOptions.Quantiles))
} }
for i, quantile := range mapping.quantiles { for i, quantile := range mapping.quantiles {
if quantile.Quantile != m.Quantiles[i].Quantile { if quantile.Quantile != m.SummaryOptions.Quantiles[i].Quantile {
t.Fatalf("%d.%q: Expected quantile %v, got %v", i, metric, m.Quantiles[i].Quantile, quantile.Quantile) t.Fatalf("%d.%q: Expected quantile %v, got %v", i, metric, m.SummaryOptions.Quantiles[i].Quantile, quantile.Quantile)
} }
if quantile.Error != m.Quantiles[i].Error { if quantile.Error != m.SummaryOptions.Quantiles[i].Error {
t.Fatalf("%d.%q: Expected Error margin %v, got %v", i, metric, m.Quantiles[i].Error, quantile.Error) t.Fatalf("%d.%q: Expected Error margin %v, got %v", i, metric, m.SummaryOptions.Quantiles[i].Error, quantile.Error)
} }
} }
} }

View file

@ -278,8 +278,8 @@ func (r *registry) getHistogram(metricName string, labels prometheus.Labels, hel
if vh == nil { if vh == nil {
metricsCount.WithLabelValues("histogram").Inc() metricsCount.WithLabelValues("histogram").Inc()
buckets := r.mapper.Defaults.Buckets buckets := r.mapper.Defaults.Buckets
if mapping.Buckets != nil && len(mapping.Buckets) > 0 { if mapping.HistogramOptions != nil && len(mapping.HistogramOptions.Buckets) > 0 {
buckets = mapping.Buckets buckets = mapping.HistogramOptions.Buckets
} }
histogramVec = prometheus.NewHistogramVec(prometheus.HistogramOpts{ histogramVec = prometheus.NewHistogramVec(prometheus.HistogramOpts{
Name: metricName, Name: metricName,
@ -325,8 +325,12 @@ func (r *registry) getSummary(metricName string, labels prometheus.Labels, help
if vh == nil { if vh == nil {
metricsCount.WithLabelValues("summary").Inc() metricsCount.WithLabelValues("summary").Inc()
quantiles := r.mapper.Defaults.Quantiles quantiles := r.mapper.Defaults.Quantiles
if mapping != nil && mapping.Quantiles != nil && len(mapping.Quantiles) > 0 { if mapping != nil && mapping.SummaryOptions != nil && len(mapping.SummaryOptions.Quantiles) > 0 {
quantiles = mapping.Quantiles quantiles = mapping.SummaryOptions.Quantiles
}
summaryOptions := mapper.SummaryOptions{}
if mapping != nil && mapping.SummaryOptions != nil {
summaryOptions = *mapping.SummaryOptions
} }
objectives := make(map[float64]float64) objectives := make(map[float64]float64)
for _, q := range quantiles { for _, q := range quantiles {