Skip to content

Commit

Permalink
Merge pull request #66 from bakins/extended-config
Browse files Browse the repository at this point in the history
Allow histograms for timer metrics
  • Loading branch information
juliusv authored Aug 3, 2017
2 parents a31feb5 + 9bca336 commit aeab290
Show file tree
Hide file tree
Showing 22 changed files with 9,955 additions and 91 deletions.
70 changes: 70 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,76 @@ follows:
test.web-server.foo.bar
=> test_web__server_foo_bar{}


YAML may also be used for the configuration if the passed filename ends in `.yml` or
`.yaml`. The above example mapping, in YAML, would be:

```yaml
mappings:
- match: test.dispatcher.*.*.*
labels:
name: "dispatcher_events_total"
processor: "$1"
action: "$2"
outcome: "$3"
job: "test_dispatcher"
- match: *.signup.*.*
labels:
name: "signup_events_total"
provider: "$2"
outcome: "$3"
job: "${1}_server"
```
Using the YAML configuration, one may also set the timer type to "histogram". The
default is "summary" as in the plain text configuration format. For example,
to set the timer type for a single metric:
```yaml
mappings:
- match: test.timing.*.*.*
timer_type: histogram
buckets: [ 0.01, 0.025, 0.05, 0.1 ]
labels:
name: "my_timer"
provider: "$2"
outcome: "$3"
job: "${1}_server"
```
Note, that one may also set the histogram buckets. If not set, then the default
[Prometheus client values](https://godoc.org/github.com/prometheus/client_golang/prometheus#pkg-variables) are used: `[.005, .01, .025, .05, .1, .25, .5, 1, 2.5, 5, 10]`. `+Inf` is added
automatically.

`timer_type` is only used when the statsd metric type is a timer. `buckets` is
only used when the statsd metric type is a timerand the `timer_type` is set to
"histogram."

One may also set defaults for the timer type and the buckets. These will be used
by all mappings that do not define these.

```yaml
defaults:
timer_type: histogram
buckets: [.005, .01, .025, .05, .1, .25, .5, 1, 2.5 ]
mappings:
# This will be a histogram using the buckets set in `defaults`.
- match: test.timing.*.*.*
labels:
name: "my_timer"
provider: "$2"
outcome: "$3"
job: "${1}_server"
# This will be a summary timer.
- match: other.timing.*.*.*
timer_type: summary
labels:
name: "other_timer"
provider: "$2"
outcome: "$3"
job: "${1}_server_other"
```
## Using Docker
You can deploy this exporter using the [prom/statsd-exporter](https://registry.hub.docker.com/u/prom/statsd-exporter/) Docker image.
Expand Down
106 changes: 85 additions & 21 deletions exporter.go
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,41 @@ func (c *SummaryContainer) Get(metricName string, labels prometheus.Labels) (pro
return summary, nil
}

type HistogramContainer struct {
Elements map[uint64]prometheus.Histogram
mapper *metricMapper
}

func NewHistogramContainer(mapper *metricMapper) *HistogramContainer {
return &HistogramContainer{
Elements: make(map[uint64]prometheus.Histogram),
mapper: mapper,
}
}

func (c *HistogramContainer) Get(metricName string, labels prometheus.Labels, mapping *metricMapping) (prometheus.Histogram, error) {
hash := hashNameAndLabels(metricName, labels)
histogram, ok := c.Elements[hash]
if !ok {
buckets := c.mapper.Defaults.Buckets
if mapping != nil && mapping.Buckets != nil && len(mapping.Buckets) > 0 {
buckets = mapping.Buckets
}
histogram = prometheus.NewHistogram(
prometheus.HistogramOpts{
Name: metricName,
Help: defaultHelp,
ConstLabels: labels,
Buckets: buckets,
})
c.Elements[hash] = histogram
if err := prometheus.Register(histogram); err != nil {
return nil, err
}
}
return histogram, nil
}

type Event interface {
MetricName() string
Value() float64
Expand Down Expand Up @@ -183,11 +218,12 @@ func (c *TimerEvent) Labels() map[string]string { return c.labels }
type Events []Event

type Exporter struct {
Counters *CounterContainer
Gauges *GaugeContainer
Summaries *SummaryContainer
mapper *metricMapper
addSuffix bool
Counters *CounterContainer
Gauges *GaugeContainer
Summaries *SummaryContainer
Histograms *HistogramContainer
mapper *metricMapper
addSuffix bool
}

func escapeMetricName(metricName string) string {
Expand Down Expand Up @@ -220,7 +256,7 @@ func (b *Exporter) Listen(e <-chan Events) {
metricName := ""
prometheusLabels := event.Labels()

labels, present := b.mapper.getMapping(event.MetricName())
mapping, labels, present := b.mapper.getMapping(event.MetricName())
if present {
metricName = labels["name"]
for label, value := range labels {
Expand Down Expand Up @@ -275,17 +311,44 @@ func (b *Exporter) Listen(e <-chan Events) {
}

case *TimerEvent:
summary, err := b.Summaries.Get(
b.suffix(metricName, "timer"),
prometheusLabels,
)
if err == nil {
summary.Observe(event.Value())
t := timerTypeDefault
if mapping != nil {
t = mapping.TimerType
}
if t == timerTypeDefault {
t = b.mapper.Defaults.TimerType
}

eventStats.WithLabelValues("timer").Inc()
} else {
log.Errorf(regErrF, metricName, err)
conflictingEventStats.WithLabelValues("timer").Inc()
switch t {
case timerTypeHistogram:
histogram, err := b.Histograms.Get(
b.suffix(metricName, "timer"),
prometheusLabels,
mapping,
)
if err == nil {
histogram.Observe(event.Value())
eventStats.WithLabelValues("timer").Inc()
} else {
log.Errorf(regErrF, metricName, err)
conflictingEventStats.WithLabelValues("timer").Inc()
}

case timerTypeDefault, timerTypeSummary:
summary, err := b.Summaries.Get(
b.suffix(metricName, "timer"),
prometheusLabels,
)
if err == nil {
summary.Observe(event.Value())
eventStats.WithLabelValues("timer").Inc()
} else {
log.Errorf(regErrF, metricName, err)
conflictingEventStats.WithLabelValues("timer").Inc()
}

default:
panic(fmt.Sprintf("unknown timer type '%s'", t))
}

default:
Expand All @@ -298,11 +361,12 @@ func (b *Exporter) Listen(e <-chan Events) {

func NewExporter(mapper *metricMapper, addSuffix bool) *Exporter {
return &Exporter{
addSuffix: addSuffix,
Counters: NewCounterContainer(),
Gauges: NewGaugeContainer(),
Summaries: NewSummaryContainer(),
mapper: mapper,
addSuffix: addSuffix,
Counters: NewCounterContainer(),
Gauges: NewGaugeContainer(),
Summaries: NewSummaryContainer(),
Histograms: NewHistogramContainer(mapper),
mapper: mapper,
}
}

Expand Down
Loading

0 comments on commit aeab290

Please sign in to comment.