2020-03-17 19:57:24 +00:00
|
|
|
// Copyright 2013 The Prometheus Authors
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
// you may not use this file except in compliance with the License.
|
|
|
|
// You may obtain a copy of the License at
|
|
|
|
//
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
//
|
|
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
// limitations under the License.
|
|
|
|
|
2020-03-16 17:03:05 +00:00
|
|
|
package exporter
|
|
|
|
|
|
|
|
import (
|
|
|
|
"os"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/go-kit/kit/log"
|
|
|
|
"github.com/go-kit/kit/log/level"
|
|
|
|
"github.com/prometheus/client_golang/prometheus"
|
|
|
|
"github.com/prometheus/statsd_exporter/pkg/clock"
|
|
|
|
"github.com/prometheus/statsd_exporter/pkg/event"
|
|
|
|
"github.com/prometheus/statsd_exporter/pkg/mapper"
|
|
|
|
"github.com/prometheus/statsd_exporter/pkg/registry"
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
defaultHelp = "Metric autogenerated by statsd_exporter."
|
|
|
|
regErrF = "Failed to update metric"
|
|
|
|
)
|
|
|
|
|
|
|
|
type Exporter struct {
|
2020-04-08 18:59:19 +00:00
|
|
|
Mapper *mapper.MetricMapper
|
|
|
|
Registry *registry.Registry
|
|
|
|
Logger log.Logger
|
|
|
|
EventsActions *prometheus.CounterVec
|
|
|
|
EventsUnmapped prometheus.Counter
|
|
|
|
ErrorEventStats *prometheus.CounterVec
|
|
|
|
EventStats *prometheus.CounterVec
|
|
|
|
ConflictingEventStats *prometheus.CounterVec
|
|
|
|
MetricsCount *prometheus.GaugeVec
|
2020-03-16 17:03:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Listen handles all events sent to the given channel sequentially. It
|
|
|
|
// terminates when the channel is closed.
|
2020-04-08 18:59:19 +00:00
|
|
|
func (b *Exporter) Listen(e <-chan event.Events) {
|
2020-03-16 17:03:05 +00:00
|
|
|
|
|
|
|
removeStaleMetricsTicker := clock.NewTicker(time.Second)
|
|
|
|
|
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case <-removeStaleMetricsTicker.C:
|
|
|
|
b.Registry.RemoveStaleMetrics()
|
|
|
|
case events, ok := <-e:
|
|
|
|
if !ok {
|
|
|
|
level.Debug(b.Logger).Log("msg", "Channel is closed. Break out of Exporter.Listener.")
|
|
|
|
removeStaleMetricsTicker.Stop()
|
|
|
|
return
|
|
|
|
}
|
|
|
|
for _, event := range events {
|
2020-04-08 18:59:19 +00:00
|
|
|
b.handleEvent(event)
|
2020-03-16 17:03:05 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// handleEvent processes a single Event according to the configured mapping.
|
2020-04-08 18:59:19 +00:00
|
|
|
func (b *Exporter) handleEvent(thisEvent event.Event) {
|
2020-03-16 17:03:05 +00:00
|
|
|
|
|
|
|
mapping, labels, present := b.Mapper.GetMapping(thisEvent.MetricName(), thisEvent.MetricType())
|
|
|
|
if mapping == nil {
|
|
|
|
mapping = &mapper.MetricMapping{}
|
|
|
|
if b.Mapper.Defaults.Ttl != 0 {
|
|
|
|
mapping.Ttl = b.Mapper.Defaults.Ttl
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if mapping.Action == mapper.ActionTypeDrop {
|
2020-04-08 18:59:19 +00:00
|
|
|
b.EventsActions.WithLabelValues("drop").Inc()
|
2020-03-16 17:03:05 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
metricName := ""
|
|
|
|
|
|
|
|
help := defaultHelp
|
|
|
|
if mapping.HelpText != "" {
|
|
|
|
help = mapping.HelpText
|
|
|
|
}
|
|
|
|
|
|
|
|
prometheusLabels := thisEvent.Labels()
|
|
|
|
if present {
|
|
|
|
if mapping.Name == "" {
|
|
|
|
level.Debug(b.Logger).Log("msg", "The mapping generates an empty metric name", "metric_name", thisEvent.MetricName(), "match", mapping.Match)
|
2020-04-08 18:59:19 +00:00
|
|
|
b.ErrorEventStats.WithLabelValues("empty_metric_name").Inc()
|
2020-03-16 17:03:05 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
metricName = mapper.EscapeMetricName(mapping.Name)
|
|
|
|
for label, value := range labels {
|
|
|
|
prometheusLabels[label] = value
|
|
|
|
}
|
2020-04-08 18:59:19 +00:00
|
|
|
b.EventsActions.WithLabelValues(string(mapping.Action)).Inc()
|
2020-03-16 17:03:05 +00:00
|
|
|
} else {
|
2020-04-08 18:59:19 +00:00
|
|
|
b.EventsUnmapped.Inc()
|
2020-03-16 17:03:05 +00:00
|
|
|
metricName = mapper.EscapeMetricName(thisEvent.MetricName())
|
|
|
|
}
|
|
|
|
|
|
|
|
switch ev := thisEvent.(type) {
|
|
|
|
case *event.CounterEvent:
|
|
|
|
// We don't accept negative values for counters. Incrementing the counter with a negative number
|
|
|
|
// will cause the exporter to panic. Instead we will warn and continue to the next event.
|
|
|
|
if thisEvent.Value() < 0.0 {
|
|
|
|
level.Debug(b.Logger).Log("msg", "counter must be non-negative value", "metric", metricName, "event_value", thisEvent.Value())
|
2020-04-08 18:59:19 +00:00
|
|
|
b.ErrorEventStats.WithLabelValues("illegal_negative_counter").Inc()
|
2020-03-16 17:03:05 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-04-08 18:59:19 +00:00
|
|
|
counter, err := b.Registry.GetCounter(metricName, prometheusLabels, help, mapping, b.MetricsCount)
|
2020-03-16 17:03:05 +00:00
|
|
|
if err == nil {
|
|
|
|
counter.Add(thisEvent.Value())
|
2020-04-08 18:59:19 +00:00
|
|
|
b.EventStats.WithLabelValues("counter").Inc()
|
2020-03-16 17:03:05 +00:00
|
|
|
} else {
|
|
|
|
level.Debug(b.Logger).Log("msg", regErrF, "metric", metricName, "error", err)
|
2020-04-08 18:59:19 +00:00
|
|
|
b.ConflictingEventStats.WithLabelValues("counter").Inc()
|
2020-03-16 17:03:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
case *event.GaugeEvent:
|
2020-04-08 18:59:19 +00:00
|
|
|
gauge, err := b.Registry.GetGauge(metricName, prometheusLabels, help, mapping, b.MetricsCount)
|
2020-03-16 17:03:05 +00:00
|
|
|
|
|
|
|
if err == nil {
|
|
|
|
if ev.GRelative {
|
|
|
|
gauge.Add(thisEvent.Value())
|
|
|
|
} else {
|
|
|
|
gauge.Set(thisEvent.Value())
|
|
|
|
}
|
2020-04-08 18:59:19 +00:00
|
|
|
b.EventStats.WithLabelValues("gauge").Inc()
|
2020-03-16 17:03:05 +00:00
|
|
|
} else {
|
|
|
|
level.Debug(b.Logger).Log("msg", regErrF, "metric", metricName, "error", err)
|
2020-04-08 18:59:19 +00:00
|
|
|
b.ConflictingEventStats.WithLabelValues("gauge").Inc()
|
2020-03-16 17:03:05 +00:00
|
|
|
}
|
|
|
|
|
2020-06-12 12:42:09 +00:00
|
|
|
case *event.ObserverEvent:
|
2020-06-15 21:26:20 +00:00
|
|
|
t := mapper.ObserverTypeDefault
|
2020-03-16 17:03:05 +00:00
|
|
|
if mapping != nil {
|
2020-06-15 21:26:20 +00:00
|
|
|
t = mapping.ObserverType
|
2020-03-16 17:03:05 +00:00
|
|
|
}
|
2020-06-15 21:26:20 +00:00
|
|
|
if t == mapper.ObserverTypeDefault {
|
|
|
|
t = b.Mapper.Defaults.ObsereverType
|
2020-03-16 17:03:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
switch t {
|
2020-06-15 21:26:20 +00:00
|
|
|
case mapper.ObserverTypeHistogram:
|
2020-04-08 18:59:19 +00:00
|
|
|
histogram, err := b.Registry.GetHistogram(metricName, prometheusLabels, help, mapping, b.MetricsCount)
|
2020-03-16 17:03:05 +00:00
|
|
|
if err == nil {
|
2020-06-11 14:17:09 +00:00
|
|
|
histogram.Observe(thisEvent.Value())
|
2020-06-15 14:14:11 +00:00
|
|
|
b.EventStats.WithLabelValues("observer").Inc()
|
2020-03-16 17:03:05 +00:00
|
|
|
} else {
|
|
|
|
level.Debug(b.Logger).Log("msg", regErrF, "metric", metricName, "error", err)
|
2020-06-15 14:14:11 +00:00
|
|
|
b.ConflictingEventStats.WithLabelValues("observer").Inc()
|
2020-03-16 17:03:05 +00:00
|
|
|
}
|
|
|
|
|
2020-06-15 21:26:20 +00:00
|
|
|
case mapper.ObserverTypeDefault, mapper.ObserverTypeSummary:
|
2020-04-08 18:59:19 +00:00
|
|
|
summary, err := b.Registry.GetSummary(metricName, prometheusLabels, help, mapping, b.MetricsCount)
|
2020-03-16 17:03:05 +00:00
|
|
|
if err == nil {
|
2020-06-11 14:17:09 +00:00
|
|
|
summary.Observe(thisEvent.Value())
|
2020-06-15 14:14:11 +00:00
|
|
|
b.EventStats.WithLabelValues("observer").Inc()
|
2020-03-16 17:03:05 +00:00
|
|
|
} else {
|
|
|
|
level.Debug(b.Logger).Log("msg", regErrF, "metric", metricName, "error", err)
|
2020-06-15 14:14:11 +00:00
|
|
|
b.ConflictingEventStats.WithLabelValues("observer").Inc()
|
2020-03-16 17:03:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
default:
|
2020-06-15 14:14:11 +00:00
|
|
|
level.Error(b.Logger).Log("msg", "unknown observer type", "type", t)
|
2020-03-16 17:03:05 +00:00
|
|
|
os.Exit(1)
|
|
|
|
}
|
|
|
|
|
|
|
|
default:
|
|
|
|
level.Debug(b.Logger).Log("msg", "Unsupported event type")
|
2020-04-08 18:59:19 +00:00
|
|
|
b.EventStats.WithLabelValues("illegal").Inc()
|
2020-03-16 17:03:05 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-08 18:59:19 +00:00
|
|
|
func NewExporter(mapper *mapper.MetricMapper, logger log.Logger, eventsActions *prometheus.CounterVec, eventsUnmapped prometheus.Counter, errorEventStats *prometheus.CounterVec, eventStats *prometheus.CounterVec, conflictingEventStats *prometheus.CounterVec, metricsCount *prometheus.GaugeVec) *Exporter {
|
2020-03-16 17:03:05 +00:00
|
|
|
return &Exporter{
|
2020-04-08 18:59:19 +00:00
|
|
|
Mapper: mapper,
|
|
|
|
Registry: registry.NewRegistry(mapper),
|
|
|
|
Logger: logger,
|
|
|
|
EventsActions: eventsActions,
|
|
|
|
EventsUnmapped: eventsUnmapped,
|
|
|
|
ErrorEventStats: errorEventStats,
|
|
|
|
EventStats: eventStats,
|
|
|
|
ConflictingEventStats: conflictingEventStats,
|
|
|
|
MetricsCount: metricsCount,
|
2020-03-16 17:03:05 +00:00
|
|
|
}
|
|
|
|
}
|