feat: configurable quantiles for Summaries

* Quantile values for a Summary are now configurable.

* The default quantiles (DefObjectives) in the prometheus Go client library is
deprecated. This commit removes the reliance on it.

Signed-off-by: Harry Bagdi <harrybagdi@gmail.com>
This commit is contained in:
Harry Bagdi 2018-08-08 19:41:41 -07:00
parent 0ffa3f9865
commit a144b1f9f7
4 changed files with 91 additions and 6 deletions

View file

@ -176,6 +176,15 @@ mappings:
provider: "$2"
outcome: "$3"
job: "${1}_server"
quantiles: # Optionally configure quantiles for your summaries
- quantile: 0.99 # https://prometheus.io/docs/practices/histograms/#quantiles
error: 0.001
- quantile: 0.95
error: 0.01
- quantile: 0.9
error: 0.05
- quantile: 0.5
error: 0.005
```
Another capability when using YAML configuration is the ability to define matches

View file

@ -117,23 +117,34 @@ func (c *GaugeContainer) Get(metricName string, labels prometheus.Labels, help s
type SummaryContainer struct {
Elements map[uint64]prometheus.Summary
mapper *metricMapper
}
func NewSummaryContainer() *SummaryContainer {
func NewSummaryContainer(mapper *metricMapper) *SummaryContainer {
return &SummaryContainer{
Elements: make(map[uint64]prometheus.Summary),
mapper: mapper,
}
}
func (c *SummaryContainer) Get(metricName string, labels prometheus.Labels, help string) (prometheus.Summary, error) {
func (c *SummaryContainer) Get(metricName string, labels prometheus.Labels, help string, mapping *metricMapping) (prometheus.Summary, error) {
hash := hashNameAndLabels(metricName, labels)
summary, ok := c.Elements[hash]
if !ok {
quantiles := c.mapper.Defaults.Quantiles
if mapping != nil && mapping.Quantiles != nil && len(mapping.Quantiles) > 0 {
quantiles = mapping.Quantiles
}
objectives := make(map[float64]float64)
for _, q := range quantiles {
objectives[q.Quantile] = q.Error
}
summary = prometheus.NewSummary(
prometheus.SummaryOpts{
Name: metricName,
Help: help,
ConstLabels: labels,
Objectives: objectives,
})
if err := prometheus.Register(summary); err != nil {
return nil, err
@ -350,6 +361,7 @@ func (b *Exporter) Listen(e <-chan Events) {
metricName,
prometheusLabels,
help,
mapping,
)
if err == nil {
summary.Observe(event.Value())
@ -375,7 +387,7 @@ func NewExporter(mapper *metricMapper) *Exporter {
return &Exporter{
Counters: NewCounterContainer(),
Gauges: NewGaugeContainer(),
Summaries: NewSummaryContainer(),
Summaries: NewSummaryContainer(mapper),
Histograms: NewHistogramContainer(mapper),
mapper: mapper,
}

View file

@ -34,9 +34,10 @@ var (
)
type mapperConfigDefaults struct {
TimerType timerType `yaml:"timer_type"`
Buckets []float64 `yaml:"buckets"`
MatchType matchType `yaml:"match_type"`
TimerType timerType `yaml:"timer_type"`
Buckets []float64 `yaml:"buckets"`
Quantiles []metricObjective `yaml:"quantiles"`
MatchType matchType `yaml:"match_type"`
}
type metricMapper struct {
@ -54,12 +55,24 @@ type metricMapping struct {
Labels prometheus.Labels `yaml:"labels"`
TimerType timerType `yaml:"timer_type"`
Buckets []float64 `yaml:"buckets"`
Quantiles []metricObjective `yaml:"quantiles"`
MatchType matchType `yaml:"match_type"`
HelpText string `yaml:"help"`
Action actionType `yaml:"action"`
MatchMetricType metricType `yaml:"match_metric_type"`
}
type metricObjective struct {
Quantile float64 `yaml:"quantile"`
Error float64 `yaml:"error"`
}
var defaultQuantiles = []metricObjective{
{Quantile: 0.5, Error: 0.05},
{Quantile: 0.9, Error: 0.01},
{Quantile: 0.99, Error: 0.001},
}
func (m *metricMapper) initFromYAMLString(fileContents string) error {
var n metricMapper
@ -71,6 +84,10 @@ func (m *metricMapper) initFromYAMLString(fileContents string) error {
n.Defaults.Buckets = prometheus.DefBuckets
}
if n.Defaults.Quantiles == nil || len(n.Defaults.Quantiles) == 0 {
n.Defaults.Quantiles = defaultQuantiles
}
if n.Defaults.MatchType == matchTypeDefault {
n.Defaults.MatchType = matchTypeGlob
}
@ -130,6 +147,10 @@ func (m *metricMapper) initFromYAMLString(fileContents string) error {
currentMapping.Buckets = n.Defaults.Buckets
}
if currentMapping.Quantiles == nil || len(currentMapping.Quantiles) == 0 {
currentMapping.Quantiles = n.Defaults.Quantiles
}
}
m.mutex.Lock()

View file

@ -20,6 +20,7 @@ import (
type mappings map[string]struct {
name string
labels map[string]string
quantiles []metricObjective
notPresent bool
}
@ -287,11 +288,40 @@ mappings:
timer_type: summary
name: "foo"
labels: {}
quantiles:
- quantile: 0.42
error: 0.04
- quantile: 0.7
error: 0.002
`,
mappings: mappings{
"test.*.*": {
name: "foo",
labels: map[string]string{},
quantiles: []metricObjective{
{Quantile: 0.42, Error: 0.04},
{Quantile: 0.7, Error: 0.002},
},
},
},
},
{
config: `---
mappings:
- match: test1.*.*
timer_type: summary
name: "foo"
labels: {}
`,
mappings: mappings{
"test1.*.*": {
name: "foo",
labels: map[string]string{},
quantiles: []metricObjective{
{Quantile: 0.5, Error: 0.05},
{Quantile: 0.9, Error: 0.01},
{Quantile: 0.99, Error: 0.001},
},
},
},
},
@ -471,6 +501,19 @@ mappings:
}
}
if len(mapping.quantiles) != 0 {
if len(mapping.quantiles) != len(m.Quantiles) {
t.Fatalf("%d.%q: Expected %d quantiles, got %d", i, metric, len(mapping.quantiles), len(m.Quantiles))
}
for i, quantile := range mapping.quantiles {
if quantile.Quantile != m.Quantiles[i].Quantile {
t.Fatalf("%d.%q: Expected quantile %v, got %v", i, metric, m.Quantiles[i].Quantile, quantile.Quantile)
}
if quantile.Error != m.Quantiles[i].Error {
t.Fatalf("%d.%q: Expected Error margin %v, got %v", i, metric, m.Quantiles[i].Error, quantile.Error)
}
}
}
}
}
}