mirror of
https://github.com/prometheus/statsd_exporter.git
synced 2024-12-24 14:30:29 +00:00
Merge pull request #135 from hbagdi/feat/summary-quantiles
feat: configurable quantiles for Summaries
This commit is contained in:
commit
b7657e8399
4 changed files with 109 additions and 7 deletions
28
README.md
28
README.md
|
@ -162,6 +162,32 @@ mappings:
|
||||||
code: "$1"
|
code: "$1"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
By default, statsd timers are represented as a Prometheus summary with
|
||||||
|
quantiles. You may optionally configure the [quantiles and acceptable
|
||||||
|
error](https://prometheus.io/docs/practices/histograms/#quantiles):
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
mappings:
|
||||||
|
- match: test.timing.*.*.*
|
||||||
|
timer_type: summary
|
||||||
|
name: "my_timer"
|
||||||
|
labels:
|
||||||
|
provider: "$2"
|
||||||
|
outcome: "$3"
|
||||||
|
job: "${1}_server"
|
||||||
|
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
|
||||||
|
```
|
||||||
|
|
||||||
|
The default quantiles are 0.99, 0.9, and 0.5.
|
||||||
|
|
||||||
In the configuration, one may also set the timer type to "histogram". The
|
In the configuration, one may also set the timer type to "histogram". The
|
||||||
default is "summary" as in the plain text configuration format. For example,
|
default is "summary" as in the plain text configuration format. For example,
|
||||||
to set the timer type for a single metric:
|
to set the timer type for a single metric:
|
||||||
|
@ -204,7 +230,7 @@ automatically.
|
||||||
only used when the statsd metric type is a timerand the `timer_type` is set to
|
only used when the statsd metric type is a timerand the `timer_type` is set to
|
||||||
"histogram."
|
"histogram."
|
||||||
|
|
||||||
One may also set defaults for the timer type, buckets and match_type. These will be used
|
One may also set defaults for the timer type, buckets or quantiles, and match_type. These will be used
|
||||||
by all mappings that do not define these.
|
by all mappings that do not define these.
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
|
|
18
exporter.go
18
exporter.go
|
@ -117,23 +117,34 @@ func (c *GaugeContainer) Get(metricName string, labels prometheus.Labels, help s
|
||||||
|
|
||||||
type SummaryContainer struct {
|
type SummaryContainer struct {
|
||||||
Elements map[uint64]prometheus.Summary
|
Elements map[uint64]prometheus.Summary
|
||||||
|
mapper *metricMapper
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSummaryContainer() *SummaryContainer {
|
func NewSummaryContainer(mapper *metricMapper) *SummaryContainer {
|
||||||
return &SummaryContainer{
|
return &SummaryContainer{
|
||||||
Elements: make(map[uint64]prometheus.Summary),
|
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)
|
hash := hashNameAndLabels(metricName, labels)
|
||||||
summary, ok := c.Elements[hash]
|
summary, ok := c.Elements[hash]
|
||||||
if !ok {
|
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(
|
summary = prometheus.NewSummary(
|
||||||
prometheus.SummaryOpts{
|
prometheus.SummaryOpts{
|
||||||
Name: metricName,
|
Name: metricName,
|
||||||
Help: help,
|
Help: help,
|
||||||
ConstLabels: labels,
|
ConstLabels: labels,
|
||||||
|
Objectives: objectives,
|
||||||
})
|
})
|
||||||
if err := prometheus.Register(summary); err != nil {
|
if err := prometheus.Register(summary); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -350,6 +361,7 @@ func (b *Exporter) Listen(e <-chan Events) {
|
||||||
metricName,
|
metricName,
|
||||||
prometheusLabels,
|
prometheusLabels,
|
||||||
help,
|
help,
|
||||||
|
mapping,
|
||||||
)
|
)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
summary.Observe(event.Value())
|
summary.Observe(event.Value())
|
||||||
|
@ -375,7 +387,7 @@ func NewExporter(mapper *metricMapper) *Exporter {
|
||||||
return &Exporter{
|
return &Exporter{
|
||||||
Counters: NewCounterContainer(),
|
Counters: NewCounterContainer(),
|
||||||
Gauges: NewGaugeContainer(),
|
Gauges: NewGaugeContainer(),
|
||||||
Summaries: NewSummaryContainer(),
|
Summaries: NewSummaryContainer(mapper),
|
||||||
Histograms: NewHistogramContainer(mapper),
|
Histograms: NewHistogramContainer(mapper),
|
||||||
mapper: mapper,
|
mapper: mapper,
|
||||||
}
|
}
|
||||||
|
|
21
mapper.go
21
mapper.go
|
@ -36,6 +36,7 @@ var (
|
||||||
type mapperConfigDefaults struct {
|
type mapperConfigDefaults struct {
|
||||||
TimerType timerType `yaml:"timer_type"`
|
TimerType timerType `yaml:"timer_type"`
|
||||||
Buckets []float64 `yaml:"buckets"`
|
Buckets []float64 `yaml:"buckets"`
|
||||||
|
Quantiles []metricObjective `yaml:"quantiles"`
|
||||||
MatchType matchType `yaml:"match_type"`
|
MatchType matchType `yaml:"match_type"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,12 +55,24 @@ type metricMapping struct {
|
||||||
Labels prometheus.Labels `yaml:"labels"`
|
Labels prometheus.Labels `yaml:"labels"`
|
||||||
TimerType timerType `yaml:"timer_type"`
|
TimerType timerType `yaml:"timer_type"`
|
||||||
Buckets []float64 `yaml:"buckets"`
|
Buckets []float64 `yaml:"buckets"`
|
||||||
|
Quantiles []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"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 {
|
func (m *metricMapper) initFromYAMLString(fileContents string) error {
|
||||||
var n metricMapper
|
var n metricMapper
|
||||||
|
|
||||||
|
@ -71,6 +84,10 @@ func (m *metricMapper) initFromYAMLString(fileContents string) error {
|
||||||
n.Defaults.Buckets = prometheus.DefBuckets
|
n.Defaults.Buckets = prometheus.DefBuckets
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if n.Defaults.Quantiles == nil || len(n.Defaults.Quantiles) == 0 {
|
||||||
|
n.Defaults.Quantiles = defaultQuantiles
|
||||||
|
}
|
||||||
|
|
||||||
if n.Defaults.MatchType == matchTypeDefault {
|
if n.Defaults.MatchType == matchTypeDefault {
|
||||||
n.Defaults.MatchType = matchTypeGlob
|
n.Defaults.MatchType = matchTypeGlob
|
||||||
}
|
}
|
||||||
|
@ -130,6 +147,10 @@ func (m *metricMapper) initFromYAMLString(fileContents string) error {
|
||||||
currentMapping.Buckets = n.Defaults.Buckets
|
currentMapping.Buckets = n.Defaults.Buckets
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if currentMapping.Quantiles == nil || len(currentMapping.Quantiles) == 0 {
|
||||||
|
currentMapping.Quantiles = n.Defaults.Quantiles
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
m.mutex.Lock()
|
m.mutex.Lock()
|
||||||
|
|
|
@ -20,6 +20,7 @@ import (
|
||||||
type mappings map[string]struct {
|
type mappings map[string]struct {
|
||||||
name string
|
name string
|
||||||
labels map[string]string
|
labels map[string]string
|
||||||
|
quantiles []metricObjective
|
||||||
notPresent bool
|
notPresent bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -287,11 +288,40 @@ mappings:
|
||||||
timer_type: summary
|
timer_type: summary
|
||||||
name: "foo"
|
name: "foo"
|
||||||
labels: {}
|
labels: {}
|
||||||
|
quantiles:
|
||||||
|
- quantile: 0.42
|
||||||
|
error: 0.04
|
||||||
|
- quantile: 0.7
|
||||||
|
error: 0.002
|
||||||
`,
|
`,
|
||||||
mappings: mappings{
|
mappings: mappings{
|
||||||
"test.*.*": {
|
"test.*.*": {
|
||||||
name: "foo",
|
name: "foo",
|
||||||
labels: map[string]string{},
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue