diff --git a/exporter.go b/exporter.go index dab584c..6af801a 100644 --- a/exporter.go +++ b/exporter.go @@ -29,6 +29,8 @@ import ( "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/common/log" "github.com/prometheus/common/model" + + "github.com/prometheus/statsd_exporter/mapper" ) const ( @@ -117,17 +119,17 @@ func (c *GaugeContainer) Get(metricName string, labels prometheus.Labels, help s type SummaryContainer struct { Elements map[uint64]prometheus.Summary - mapper *metricMapper + mapper *mapper.MetricMapper } -func NewSummaryContainer(mapper *metricMapper) *SummaryContainer { +func NewSummaryContainer(mapper *mapper.MetricMapper) *SummaryContainer { return &SummaryContainer{ Elements: make(map[uint64]prometheus.Summary), mapper: mapper, } } -func (c *SummaryContainer) Get(metricName string, labels prometheus.Labels, help string, mapping *metricMapping) (prometheus.Summary, error) { +func (c *SummaryContainer) Get(metricName string, labels prometheus.Labels, help string, mapping *mapper.MetricMapping) (prometheus.Summary, error) { hash := hashNameAndLabels(metricName, labels) summary, ok := c.Elements[hash] if !ok { @@ -156,17 +158,17 @@ func (c *SummaryContainer) Get(metricName string, labels prometheus.Labels, help type HistogramContainer struct { Elements map[uint64]prometheus.Histogram - mapper *metricMapper + mapper *mapper.MetricMapper } -func NewHistogramContainer(mapper *metricMapper) *HistogramContainer { +func NewHistogramContainer(mapper *mapper.MetricMapper) *HistogramContainer { return &HistogramContainer{ Elements: make(map[uint64]prometheus.Histogram), mapper: mapper, } } -func (c *HistogramContainer) Get(metricName string, labels prometheus.Labels, help string, mapping *metricMapping) (prometheus.Histogram, error) { +func (c *HistogramContainer) Get(metricName string, labels prometheus.Labels, help string, mapping *mapper.MetricMapping) (prometheus.Histogram, error) { hash := hashNameAndLabels(metricName, labels) histogram, ok := c.Elements[hash] if !ok { @@ -193,7 +195,7 @@ type Event interface { MetricName() string Value() float64 Labels() map[string]string - MetricType() metricType + MetricType() mapper.MetricType } type CounterEvent struct { @@ -202,10 +204,10 @@ type CounterEvent struct { labels map[string]string } -func (c *CounterEvent) MetricName() string { return c.metricName } -func (c *CounterEvent) Value() float64 { return c.value } -func (c *CounterEvent) Labels() map[string]string { return c.labels } -func (c *CounterEvent) MetricType() metricType { return metricTypeCounter } +func (c *CounterEvent) MetricName() string { return c.metricName } +func (c *CounterEvent) Value() float64 { return c.value } +func (c *CounterEvent) Labels() map[string]string { return c.labels } +func (c *CounterEvent) MetricType() mapper.MetricType { return mapper.MetricTypeCounter } type GaugeEvent struct { metricName string @@ -214,10 +216,10 @@ type GaugeEvent struct { labels map[string]string } -func (g *GaugeEvent) MetricName() string { return g.metricName } -func (g *GaugeEvent) Value() float64 { return g.value } -func (c *GaugeEvent) Labels() map[string]string { return c.labels } -func (c *GaugeEvent) MetricType() metricType { return metricTypeGauge } +func (g *GaugeEvent) MetricName() string { return g.metricName } +func (g *GaugeEvent) Value() float64 { return g.value } +func (c *GaugeEvent) Labels() map[string]string { return c.labels } +func (c *GaugeEvent) MetricType() mapper.MetricType { return mapper.MetricTypeGauge } type TimerEvent struct { metricName string @@ -225,10 +227,10 @@ type TimerEvent struct { labels map[string]string } -func (t *TimerEvent) MetricName() string { return t.metricName } -func (t *TimerEvent) Value() float64 { return t.value } -func (c *TimerEvent) Labels() map[string]string { return c.labels } -func (c *TimerEvent) MetricType() metricType { return metricTypeTimer } +func (t *TimerEvent) MetricName() string { return t.metricName } +func (t *TimerEvent) Value() float64 { return t.value } +func (c *TimerEvent) Labels() map[string]string { return c.labels } +func (c *TimerEvent) MetricType() mapper.MetricType { return mapper.MetricTypeTimer } type Events []Event @@ -237,7 +239,7 @@ type Exporter struct { Gauges *GaugeContainer Summaries *SummaryContainer Histograms *HistogramContainer - mapper *metricMapper + mapper *mapper.MetricMapper } func escapeMetricName(metricName string) string { @@ -263,12 +265,12 @@ func (b *Exporter) Listen(e <-chan Events) { metricName := "" prometheusLabels := event.Labels() - mapping, labels, present := b.mapper.getMapping(event.MetricName(), event.MetricType()) + mapping, labels, present := b.mapper.GetMapping(event.MetricName(), event.MetricType()) if mapping == nil { - mapping = &metricMapping{} + mapping = &mapper.MetricMapping{} } - if mapping.Action == actionTypeDrop { + if mapping.Action == mapper.ActionTypeDrop { continue } @@ -332,16 +334,16 @@ func (b *Exporter) Listen(e <-chan Events) { } case *TimerEvent: - t := timerTypeDefault + t := mapper.TimerTypeDefault if mapping != nil { t = mapping.TimerType } - if t == timerTypeDefault { + if t == mapper.TimerTypeDefault { t = b.mapper.Defaults.TimerType } switch t { - case timerTypeHistogram: + case mapper.TimerTypeHistogram: histogram, err := b.Histograms.Get( metricName, prometheusLabels, @@ -356,7 +358,7 @@ func (b *Exporter) Listen(e <-chan Events) { conflictingEventStats.WithLabelValues("timer").Inc() } - case timerTypeDefault, timerTypeSummary: + case mapper.TimerTypeDefault, mapper.TimerTypeSummary: summary, err := b.Summaries.Get( metricName, prometheusLabels, @@ -383,7 +385,7 @@ func (b *Exporter) Listen(e <-chan Events) { } } -func NewExporter(mapper *metricMapper) *Exporter { +func NewExporter(mapper *mapper.MetricMapper) *Exporter { return &Exporter{ Counters: NewCounterContainer(), Gauges: NewGaugeContainer(), diff --git a/exporter_test.go b/exporter_test.go index 9941f0e..8f136c0 100644 --- a/exporter_test.go +++ b/exporter_test.go @@ -20,6 +20,8 @@ import ( "time" "github.com/prometheus/client_golang/prometheus" + + "github.com/prometheus/statsd_exporter/mapper" ) // TestNegativeCounter validates when we send a negative @@ -44,7 +46,7 @@ func TestNegativeCounter(t *testing.T) { }, } events <- c - ex := NewExporter(&metricMapper{}) + ex := NewExporter(&mapper.MetricMapper{}) // Close channel to signify we are done with the listener after a short period. go func() { @@ -60,7 +62,7 @@ func TestNegativeCounter(t *testing.T) { // It sends the same tags first with a valid value, then with an invalid one. // The exporter should not panic, but drop the invalid event func TestInvalidUtf8InDatadogTagValue(t *testing.T) { - ex := NewExporter(&metricMapper{}) + ex := NewExporter(&mapper.MetricMapper{}) for _, l := range []statsDPacketHandler{&StatsDUDPListener{}, &mockStatsDTCPListener{}} { events := make(chan Events, 2) @@ -96,8 +98,8 @@ func TestHistogramUnits(t *testing.T) { }, } events <- c - ex := NewExporter(&metricMapper{}) - ex.mapper.Defaults.TimerType = timerTypeHistogram + ex := NewExporter(&mapper.MetricMapper{}) + ex.mapper.Defaults.TimerType = mapper.TimerTypeHistogram // Close channel to signify we are done with the listener after a short period. go func() { diff --git a/main.go b/main.go index ea92b69..3b55c0d 100644 --- a/main.go +++ b/main.go @@ -25,6 +25,8 @@ import ( "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/common/log" "github.com/prometheus/common/version" + + "github.com/prometheus/statsd_exporter/mapper" ) func init() { @@ -96,7 +98,7 @@ func tcpAddrFromString(addr string) *net.TCPAddr { } } -func watchConfig(fileName string, mapper *metricMapper) { +func watchConfig(fileName string, mapper *mapper.MetricMapper) { watcher, err := fsnotify.NewWatcher() if err != nil { log.Fatal(err) @@ -111,7 +113,7 @@ func watchConfig(fileName string, mapper *metricMapper) { select { case ev := <-watcher.Event: log.Infof("Config file changed (%s), attempting reload", ev) - err = mapper.initFromFile(fileName) + err = mapper.InitFromFile(fileName) if err != nil { log.Errorln("Error reloading config:", err) configLoads.WithLabelValues("failure").Inc() @@ -186,9 +188,9 @@ func main() { go tl.Listen(events) } - mapper := &metricMapper{} + mapper := &mapper.MetricMapper{} if *mappingConfig != "" { - err := mapper.initFromFile(*mappingConfig) + err := mapper.InitFromFile(*mappingConfig) if err != nil { log.Fatal("Error loading config:", err) } diff --git a/action.go b/mapper/action.go similarity index 69% rename from action.go rename to mapper/action.go index c1baab3..b8c0977 100644 --- a/action.go +++ b/mapper/action.go @@ -11,30 +11,30 @@ // See the License for the specific language governing permissions and // limitations under the License. -package main +package mapper import "fmt" -type actionType string +type ActionType string const ( - actionTypeMap actionType = "map" - actionTypeDrop actionType = "drop" - actionTypeDefault actionType = "" + ActionTypeMap ActionType = "map" + ActionTypeDrop ActionType = "drop" + ActionTypeDefault ActionType = "" ) -func (t *actionType) UnmarshalYAML(unmarshal func(interface{}) error) error { +func (t *ActionType) UnmarshalYAML(unmarshal func(interface{}) error) error { var v string if err := unmarshal(&v); err != nil { return err } - switch actionType(v) { - case actionTypeDrop: - *t = actionTypeDrop - case actionTypeMap, actionTypeDefault: - *t = actionTypeMap + switch ActionType(v) { + case ActionTypeDrop: + *t = ActionTypeDrop + case ActionTypeMap, ActionTypeDefault: + *t = ActionTypeMap default: return fmt.Errorf("invalid action type %q", v) } diff --git a/mapper.go b/mapper/mapper.go similarity index 84% rename from mapper.go rename to mapper/mapper.go index 2a897de..ec469a7 100644 --- a/mapper.go +++ b/mapper/mapper.go @@ -11,7 +11,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package main +package mapper import ( "fmt" @@ -34,32 +34,32 @@ var ( ) type mapperConfigDefaults struct { - TimerType timerType `yaml:"timer_type"` + TimerType TimerType `yaml:"timer_type"` Buckets []float64 `yaml:"buckets"` Quantiles []metricObjective `yaml:"quantiles"` - MatchType matchType `yaml:"match_type"` + MatchType MatchType `yaml:"match_type"` } -type metricMapper struct { +type MetricMapper struct { Defaults mapperConfigDefaults `yaml:"defaults"` - Mappings []metricMapping `yaml:"mappings"` + Mappings []MetricMapping `yaml:"mappings"` mutex sync.Mutex } type matchMetricType string -type metricMapping struct { +type MetricMapping struct { Match string `yaml:"match"` Name string `yaml:"name"` regex *regexp.Regexp Labels prometheus.Labels `yaml:"labels"` - TimerType timerType `yaml:"timer_type"` + TimerType TimerType `yaml:"timer_type"` Buckets []float64 `yaml:"buckets"` Quantiles []metricObjective `yaml:"quantiles"` - MatchType matchType `yaml:"match_type"` + MatchType MatchType `yaml:"match_type"` HelpText string `yaml:"help"` - Action actionType `yaml:"action"` - MatchMetricType metricType `yaml:"match_metric_type"` + Action ActionType `yaml:"action"` + MatchMetricType MetricType `yaml:"match_metric_type"` } type metricObjective struct { @@ -73,8 +73,8 @@ var defaultQuantiles = []metricObjective{ {Quantile: 0.99, Error: 0.001}, } -func (m *metricMapper) initFromYAMLString(fileContents string) error { - var n metricMapper +func (m *MetricMapper) InitFromYAMLString(fileContents string) error { + var n MetricMapper if err := yaml.Unmarshal([]byte(fileContents), &n); err != nil { return err @@ -88,8 +88,8 @@ func (m *metricMapper) initFromYAMLString(fileContents string) error { n.Defaults.Quantiles = defaultQuantiles } - if n.Defaults.MatchType == matchTypeDefault { - n.Defaults.MatchType = matchTypeGlob + if n.Defaults.MatchType == MatchTypeDefault { + n.Defaults.MatchType = MatchTypeGlob } for i := range n.Mappings { @@ -115,10 +115,10 @@ func (m *metricMapper) initFromYAMLString(fileContents string) error { } if currentMapping.Action == "" { - currentMapping.Action = actionTypeMap + currentMapping.Action = ActionTypeMap } - if currentMapping.MatchType == matchTypeGlob { + if currentMapping.MatchType == MatchTypeGlob { if !metricLineRE.MatchString(currentMapping.Match) { return fmt.Errorf("invalid match: %s", currentMapping.Match) } @@ -164,15 +164,15 @@ func (m *metricMapper) initFromYAMLString(fileContents string) error { return nil } -func (m *metricMapper) initFromFile(fileName string) error { +func (m *MetricMapper) InitFromFile(fileName string) error { mappingStr, err := ioutil.ReadFile(fileName) if err != nil { return err } - return m.initFromYAMLString(string(mappingStr)) + return m.InitFromYAMLString(string(mappingStr)) } -func (m *metricMapper) getMapping(statsdMetric string, statsdMetricType metricType) (*metricMapping, prometheus.Labels, bool) { +func (m *MetricMapper) GetMapping(statsdMetric string, statsdMetricType MetricType) (*MetricMapping, prometheus.Labels, bool) { m.mutex.Lock() defer m.mutex.Unlock() diff --git a/mapper_test.go b/mapper/mapper_test.go similarity index 96% rename from mapper_test.go rename to mapper/mapper_test.go index b11d5c2..79b10df 100644 --- a/mapper_test.go +++ b/mapper/mapper_test.go @@ -11,7 +11,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package main +package mapper import ( "testing" @@ -473,9 +473,9 @@ mappings: }, } - mapper := metricMapper{} + mapper := MetricMapper{} for i, scenario := range scenarios { - err := mapper.initFromYAMLString(scenario.config) + err := mapper.InitFromYAMLString(scenario.config) if err != nil && !scenario.configBad { t.Fatalf("%d. Config load error: %s %s", i, scenario.config, err) } @@ -483,9 +483,9 @@ mappings: t.Fatalf("%d. Expected bad config, but loaded ok: %s", i, scenario.config) } - var dummyMetricType metricType = "" + var dummyMetricType MetricType = "" for metric, mapping := range scenario.mappings { - m, labels, present := mapper.getMapping(metric, dummyMetricType) + m, labels, present := mapper.GetMapping(metric, dummyMetricType) if present && mapping.name != "" && m.Name != mapping.name { t.Fatalf("%d.%q: Expected name %v, got %v", i, metric, m.Name, mapping.name) } @@ -522,7 +522,7 @@ func TestAction(t *testing.T) { scenarios := []struct { config string configBad bool - expectedAction actionType + expectedAction ActionType }{ { // no action set @@ -532,7 +532,7 @@ mappings: name: "foo" `, configBad: false, - expectedAction: actionTypeMap, + expectedAction: ActionTypeMap, }, { // map action set @@ -543,7 +543,7 @@ mappings: action: map `, configBad: false, - expectedAction: actionTypeMap, + expectedAction: ActionTypeMap, }, { // drop action set @@ -554,7 +554,7 @@ mappings: action: drop `, configBad: false, - expectedAction: actionTypeDrop, + expectedAction: ActionTypeDrop, }, { // invalid action set @@ -565,13 +565,13 @@ mappings: action: xyz `, configBad: true, - expectedAction: actionTypeDrop, + expectedAction: ActionTypeDrop, }, } for i, scenario := range scenarios { - mapper := metricMapper{} - err := mapper.initFromYAMLString(scenario.config) + mapper := MetricMapper{} + err := mapper.InitFromYAMLString(scenario.config) if err != nil && !scenario.configBad { t.Fatalf("%d. Config load error: %s %s", i, scenario.config, err) } diff --git a/match.go b/mapper/match.go similarity index 69% rename from match.go rename to mapper/match.go index 23357b4..12d5e8d 100644 --- a/match.go +++ b/mapper/match.go @@ -11,29 +11,29 @@ // See the License for the specific language governing permissions and // limitations under the License. -package main +package mapper import "fmt" -type matchType string +type MatchType string const ( - matchTypeGlob matchType = "glob" - matchTypeRegex matchType = "regex" - matchTypeDefault matchType = "" + MatchTypeGlob MatchType = "glob" + MatchTypeRegex MatchType = "regex" + MatchTypeDefault MatchType = "" ) -func (t *matchType) UnmarshalYAML(unmarshal func(interface{}) error) error { +func (t *MatchType) UnmarshalYAML(unmarshal func(interface{}) error) error { var v string if err := unmarshal(&v); err != nil { return err } - switch matchType(v) { - case matchTypeRegex: - *t = matchTypeRegex - case matchTypeGlob, matchTypeDefault: - *t = matchTypeGlob + switch MatchType(v) { + case MatchTypeRegex: + *t = MatchTypeRegex + case MatchTypeGlob, MatchTypeDefault: + *t = MatchTypeGlob default: return fmt.Errorf("invalid match type %q", v) } diff --git a/metric_type.go b/mapper/metric_type.go similarity index 66% rename from metric_type.go rename to mapper/metric_type.go index 61163fd..0a0810f 100644 --- a/metric_type.go +++ b/mapper/metric_type.go @@ -11,31 +11,31 @@ // See the License for the specific language governing permissions and // limitations under the License. -package main +package mapper import "fmt" -type metricType string +type MetricType string const ( - metricTypeCounter metricType = "counter" - metricTypeGauge metricType = "gauge" - metricTypeTimer metricType = "timer" + MetricTypeCounter MetricType = "counter" + MetricTypeGauge MetricType = "gauge" + MetricTypeTimer MetricType = "timer" ) -func (m *metricType) UnmarshalYAML(unmarshal func(interface{}) error) error { +func (m *MetricType) UnmarshalYAML(unmarshal func(interface{}) error) error { var v string if err := unmarshal(&v); err != nil { return err } - switch metricType(v) { - case metricTypeCounter: - *m = metricTypeCounter - case metricTypeGauge: - *m = metricTypeGauge - case metricTypeTimer: - *m = metricTypeTimer + switch MetricType(v) { + case MetricTypeCounter: + *m = MetricTypeCounter + case MetricTypeGauge: + *m = MetricTypeGauge + case MetricTypeTimer: + *m = MetricTypeTimer default: return fmt.Errorf("invalid metric type '%s'", v) } diff --git a/mapper/telemetry.go b/mapper/telemetry.go new file mode 100644 index 0000000..c74ccf0 --- /dev/null +++ b/mapper/telemetry.go @@ -0,0 +1,29 @@ +// 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. + +package mapper + +import ( + "github.com/prometheus/client_golang/prometheus" +) + +var ( + mappingsCount = prometheus.NewGauge(prometheus.GaugeOpts{ + Name: "statsd_exporter_loaded_mappings", + Help: "The current number of configured metric mappings.", + }) +) + +func init() { + prometheus.MustRegister(mappingsCount) +} diff --git a/timer.go b/mapper/timer.go similarity index 68% rename from timer.go rename to mapper/timer.go index 8cf82d2..f1d2fb7 100644 --- a/timer.go +++ b/mapper/timer.go @@ -11,29 +11,29 @@ // See the License for the specific language governing permissions and // limitations under the License. -package main +package mapper import "fmt" -type timerType string +type TimerType string const ( - timerTypeHistogram timerType = "histogram" - timerTypeSummary timerType = "summary" - timerTypeDefault timerType = "" + TimerTypeHistogram TimerType = "histogram" + TimerTypeSummary TimerType = "summary" + TimerTypeDefault TimerType = "" ) -func (t *timerType) UnmarshalYAML(unmarshal func(interface{}) error) error { +func (t *TimerType) UnmarshalYAML(unmarshal func(interface{}) error) error { var v string if err := unmarshal(&v); err != nil { return err } - switch timerType(v) { - case timerTypeHistogram: - *t = timerTypeHistogram - case timerTypeSummary, timerTypeDefault: - *t = timerTypeSummary + switch TimerType(v) { + case TimerTypeHistogram: + *t = TimerTypeHistogram + case TimerTypeSummary, TimerTypeDefault: + *t = TimerTypeSummary default: return fmt.Errorf("invalid timer type '%s'", v) } diff --git a/telemetry.go b/telemetry.go index 92e174b..1025efc 100644 --- a/telemetry.go +++ b/telemetry.go @@ -91,10 +91,6 @@ var ( }, []string{"outcome"}, ) - mappingsCount = prometheus.NewGauge(prometheus.GaugeOpts{ - Name: "statsd_exporter_loaded_mappings", - Help: "The current number of configured metric mappings.", - }) conflictingEventStats = prometheus.NewCounterVec( prometheus.CounterOpts{ Name: "statsd_exporter_events_conflict_total", @@ -116,6 +112,5 @@ func init() { prometheus.MustRegister(tagsReceived) prometheus.MustRegister(tagErrors) prometheus.MustRegister(configLoads) - prometheus.MustRegister(mappingsCount) prometheus.MustRegister(conflictingEventStats) }