From 96eab1202717e073782ec399a4e0820cae15b1bb Mon Sep 17 00:00:00 2001 From: Christopher Speller Date: Thu, 17 Aug 2017 17:19:06 -0700 Subject: Updating server dependancies. (#7246) --- .../armon/go-metrics/circonus/circonus.go | 27 ++++ .../armon/go-metrics/circonus/circonus_test.go | 4 +- .../armon/go-metrics/datadog/dogstatsd.go | 73 ++++++---- .../armon/go-metrics/datadog/dogstatsd_test.go | 35 ++--- vendor/github.com/armon/go-metrics/inmem.go | 95 ++++++++++--- .../github.com/armon/go-metrics/inmem_endpoint.go | 118 ++++++++++++++++ .../armon/go-metrics/inmem_endpoint_test.go | 133 ++++++++++++++++++ vendor/github.com/armon/go-metrics/inmem_signal.go | 33 +++-- .../armon/go-metrics/inmem_signal_test.go | 12 ++ vendor/github.com/armon/go-metrics/inmem_test.go | 76 ++++++----- vendor/github.com/armon/go-metrics/metrics.go | 121 +++++++++++++++-- vendor/github.com/armon/go-metrics/metrics_test.go | 151 ++++++++++++++++++++- .../armon/go-metrics/prometheus/prometheus.go | 72 +++++++--- vendor/github.com/armon/go-metrics/sink.go | 32 ++++- vendor/github.com/armon/go-metrics/sink_test.go | 108 ++++++++++++++- vendor/github.com/armon/go-metrics/start.go | 37 ++++- vendor/github.com/armon/go-metrics/start_test.go | 107 ++++++++++++++- vendor/github.com/armon/go-metrics/statsd.go | 23 ++++ vendor/github.com/armon/go-metrics/statsd_test.go | 39 +++++- vendor/github.com/armon/go-metrics/statsite.go | 23 ++++ .../github.com/armon/go-metrics/statsite_test.go | 39 +++++- 21 files changed, 1194 insertions(+), 164 deletions(-) create mode 100644 vendor/github.com/armon/go-metrics/inmem_endpoint.go create mode 100644 vendor/github.com/armon/go-metrics/inmem_endpoint_test.go (limited to 'vendor/github.com/armon/go-metrics') diff --git a/vendor/github.com/armon/go-metrics/circonus/circonus.go b/vendor/github.com/armon/go-metrics/circonus/circonus.go index c6e3974b5..eb41b9945 100644 --- a/vendor/github.com/armon/go-metrics/circonus/circonus.go +++ b/vendor/github.com/armon/go-metrics/circonus/circonus.go @@ -5,6 +5,7 @@ package circonus import ( "strings" + "github.com/armon/go-metrics" cgm "github.com/circonus-labs/circonus-gometrics" ) @@ -61,6 +62,12 @@ func (s *CirconusSink) SetGauge(key []string, val float32) { s.metrics.SetGauge(flatKey, int64(val)) } +// SetGaugeWithLabels sets value for a gauge metric with the given labels +func (s *CirconusSink) SetGaugeWithLabels(key []string, val float32, labels []metrics.Label) { + flatKey := s.flattenKeyLabels(key, labels) + s.metrics.SetGauge(flatKey, int64(val)) +} + // EmitKey is not implemented in circonus func (s *CirconusSink) EmitKey(key []string, val float32) { // NOP @@ -72,12 +79,24 @@ func (s *CirconusSink) IncrCounter(key []string, val float32) { s.metrics.IncrementByValue(flatKey, uint64(val)) } +// IncrCounterWithLabels increments a counter metric with the given labels +func (s *CirconusSink) IncrCounterWithLabels(key []string, val float32, labels []metrics.Label) { + flatKey := s.flattenKeyLabels(key, labels) + s.metrics.IncrementByValue(flatKey, uint64(val)) +} + // AddSample adds a sample to a histogram metric func (s *CirconusSink) AddSample(key []string, val float32) { flatKey := s.flattenKey(key) s.metrics.RecordValue(flatKey, float64(val)) } +// AddSampleWithLabels adds a sample to a histogram metric with the given labels +func (s *CirconusSink) AddSampleWithLabels(key []string, val float32, labels []metrics.Label) { + flatKey := s.flattenKeyLabels(key, labels) + s.metrics.RecordValue(flatKey, float64(val)) +} + // Flattens key to Circonus metric name func (s *CirconusSink) flattenKey(parts []string) string { joined := strings.Join(parts, "`") @@ -90,3 +109,11 @@ func (s *CirconusSink) flattenKey(parts []string) string { } }, joined) } + +// Flattens the key along with labels for formatting, removes spaces +func (s *CirconusSink) flattenKeyLabels(parts []string, labels []metrics.Label) string { + for _, label := range labels { + parts = append(parts, label.Value) + } + return s.flattenKey(parts) +} diff --git a/vendor/github.com/armon/go-metrics/circonus/circonus_test.go b/vendor/github.com/armon/go-metrics/circonus/circonus_test.go index 234a3cb89..4eb76e411 100644 --- a/vendor/github.com/armon/go-metrics/circonus/circonus_test.go +++ b/vendor/github.com/armon/go-metrics/circonus/circonus_test.go @@ -12,7 +12,7 @@ import ( func TestNewCirconusSink(t *testing.T) { // test with invalid config (nil) - expectedError := errors.New("Invalid check manager configuration (no API token AND no submission url).") + expectedError := errors.New("invalid check manager configuration (no API token AND no submission url)") _, err := NewCirconusSink(nil) if err == nil || err.Error() != expectedError.Error() { t.Errorf("Expected an '%#v' error, got '%#v'", expectedError, err) @@ -87,7 +87,7 @@ func TestSetGauge(t *testing.T) { cs.Flush() }() - expect := "{\"foo`bar\":{\"_type\":\"n\",\"_value\":1}}" + expect := "{\"foo`bar\":{\"_type\":\"n\",\"_value\":\"1\"}}" actual := <-q if actual != expect { diff --git a/vendor/github.com/armon/go-metrics/datadog/dogstatsd.go b/vendor/github.com/armon/go-metrics/datadog/dogstatsd.go index aaba9fe0e..fe021d01c 100644 --- a/vendor/github.com/armon/go-metrics/datadog/dogstatsd.go +++ b/vendor/github.com/armon/go-metrics/datadog/dogstatsd.go @@ -5,6 +5,7 @@ import ( "strings" "github.com/DataDog/datadog-go/statsd" + "github.com/armon/go-metrics" ) // DogStatsdSink provides a MetricSink that can be used @@ -45,46 +46,49 @@ func (s *DogStatsdSink) EnableHostNamePropagation() { func (s *DogStatsdSink) flattenKey(parts []string) string { joined := strings.Join(parts, ".") - return strings.Map(func(r rune) rune { - switch r { - case ':': - fallthrough - case ' ': - return '_' - default: - return r - } - }, joined) + return strings.Map(sanitize, joined) +} + +func sanitize(r rune) rune { + switch r { + case ':': + fallthrough + case ' ': + return '_' + default: + return r + } } -func (s *DogStatsdSink) parseKey(key []string) ([]string, []string) { +func (s *DogStatsdSink) parseKey(key []string) ([]string, []metrics.Label) { // Since DogStatsd supports dimensionality via tags on metric keys, this sink's approach is to splice the hostname out of the key in favor of a `host` tag // The `host` tag is either forced here, or set downstream by the DogStatsd server - var tags []string + var labels []metrics.Label hostName := s.hostName - //Splice the hostname out of the key + // Splice the hostname out of the key for i, el := range key { if el == hostName { key = append(key[:i], key[i+1:]...) + break } } if s.propagateHostname { - tags = append(tags, fmt.Sprintf("host:%s", hostName)) + labels = append(labels, metrics.Label{"host", hostName}) } - return key, tags + return key, labels } // Implementation of methods in the MetricSink interface func (s *DogStatsdSink) SetGauge(key []string, val float32) { - s.SetGaugeWithTags(key, val, []string{}) + s.SetGaugeWithLabels(key, val, nil) } func (s *DogStatsdSink) IncrCounter(key []string, val float32) { - s.IncrCounterWithTags(key, val, []string{}) + s.IncrCounterWithLabels(key, val, nil) } // EmitKey is not implemented since DogStatsd does not provide a metric type that holds an @@ -93,33 +97,44 @@ func (s *DogStatsdSink) EmitKey(key []string, val float32) { } func (s *DogStatsdSink) AddSample(key []string, val float32) { - s.AddSampleWithTags(key, val, []string{}) + s.AddSampleWithLabels(key, val, nil) } -// The following ...WithTags methods correspond to Datadog's Tag extension to Statsd. +// The following ...WithLabels methods correspond to Datadog's Tag extension to Statsd. // http://docs.datadoghq.com/guides/dogstatsd/#tags - -func (s *DogStatsdSink) SetGaugeWithTags(key []string, val float32, tags []string) { - flatKey, tags := s.getFlatkeyAndCombinedTags(key, tags) +func (s *DogStatsdSink) SetGaugeWithLabels(key []string, val float32, labels []metrics.Label) { + flatKey, tags := s.getFlatkeyAndCombinedLabels(key, labels) rate := 1.0 s.client.Gauge(flatKey, float64(val), tags, rate) } -func (s *DogStatsdSink) IncrCounterWithTags(key []string, val float32, tags []string) { - flatKey, tags := s.getFlatkeyAndCombinedTags(key, tags) +func (s *DogStatsdSink) IncrCounterWithLabels(key []string, val float32, labels []metrics.Label) { + flatKey, tags := s.getFlatkeyAndCombinedLabels(key, labels) rate := 1.0 s.client.Count(flatKey, int64(val), tags, rate) } -func (s *DogStatsdSink) AddSampleWithTags(key []string, val float32, tags []string) { - flatKey, tags := s.getFlatkeyAndCombinedTags(key, tags) +func (s *DogStatsdSink) AddSampleWithLabels(key []string, val float32, labels []metrics.Label) { + flatKey, tags := s.getFlatkeyAndCombinedLabels(key, labels) rate := 1.0 s.client.TimeInMilliseconds(flatKey, float64(val), tags, rate) } -func (s *DogStatsdSink) getFlatkeyAndCombinedTags(key []string, tags []string) (flattenedKey string, combinedTags []string) { - key, hostTags := s.parseKey(key) +func (s *DogStatsdSink) getFlatkeyAndCombinedLabels(key []string, labels []metrics.Label) (string, []string) { + key, parsedLabels := s.parseKey(key) flatKey := s.flattenKey(key) - tags = append(tags, hostTags...) + labels = append(labels, parsedLabels...) + + var tags []string + for _, label := range labels { + label.Name = strings.Map(sanitize, label.Name) + label.Value = strings.Map(sanitize, label.Value) + if label.Value != "" { + tags = append(tags, fmt.Sprintf("%s:%s", label.Name, label.Value)) + } else { + tags = append(tags, label.Name) + } + } + return flatKey, tags } diff --git a/vendor/github.com/armon/go-metrics/datadog/dogstatsd_test.go b/vendor/github.com/armon/go-metrics/datadog/dogstatsd_test.go index 0ec51e3f1..43b81ac7f 100644 --- a/vendor/github.com/armon/go-metrics/datadog/dogstatsd_test.go +++ b/vendor/github.com/armon/go-metrics/datadog/dogstatsd_test.go @@ -1,13 +1,14 @@ package datadog import ( - "fmt" "net" "reflect" "testing" + + "github.com/armon/go-metrics" ) -var EmptyTags []string +var EmptyTags []metrics.Label const ( DogStatsdAddr = "127.0.0.1:7254" @@ -22,14 +23,14 @@ func MockGetHostname() string { var ParseKeyTests = []struct { KeyToParse []string - Tags []string + Tags []metrics.Label PropagateHostname bool ExpectedKey []string - ExpectedTags []string + ExpectedTags []metrics.Label }{ {[]string{"a", MockGetHostname(), "b", "c"}, EmptyTags, HostnameDisabled, []string{"a", "b", "c"}, EmptyTags}, {[]string{"a", "b", "c"}, EmptyTags, HostnameDisabled, []string{"a", "b", "c"}, EmptyTags}, - {[]string{"a", "b", "c"}, EmptyTags, HostnameEnabled, []string{"a", "b", "c"}, []string{fmt.Sprintf("host:%s", MockGetHostname())}}, + {[]string{"a", "b", "c"}, EmptyTags, HostnameEnabled, []string{"a", "b", "c"}, []metrics.Label{{"host", MockGetHostname()}}}, } var FlattenKeyTests = []struct { @@ -44,7 +45,7 @@ var MetricSinkTests = []struct { Method string Metric []string Value interface{} - Tags []string + Tags []metrics.Label PropagateHostname bool Expected string }{ @@ -53,13 +54,15 @@ var MetricSinkTests = []struct { {"AddSample", []string{"sample", "thing"}, float32(4), EmptyTags, HostnameDisabled, "sample.thing:4.000000|ms"}, {"IncrCounter", []string{"count", "me"}, float32(3), EmptyTags, HostnameDisabled, "count.me:3|c"}, - {"SetGauge", []string{"foo", "baz"}, float32(42), []string{"my_tag:my_value"}, HostnameDisabled, "foo.baz:42.000000|g|#my_tag:my_value"}, - {"SetGauge", []string{"foo", "bar"}, float32(42), []string{"my_tag:my_value", "other_tag:other_value"}, HostnameDisabled, "foo.bar:42.000000|g|#my_tag:my_value,other_tag:other_value"}, - {"SetGauge", []string{"foo", "bar"}, float32(42), []string{"my_tag:my_value", "other_tag:other_value"}, HostnameEnabled, "foo.bar:42.000000|g|#my_tag:my_value,other_tag:other_value,host:test_hostname"}, + {"SetGauge", []string{"foo", "baz"}, float32(42), []metrics.Label{{"my_tag", ""}}, HostnameDisabled, "foo.baz:42.000000|g|#my_tag"}, + {"SetGauge", []string{"foo", "baz"}, float32(42), []metrics.Label{{"my tag", "my_value"}}, HostnameDisabled, "foo.baz:42.000000|g|#my_tag:my_value"}, + {"SetGauge", []string{"foo", "bar"}, float32(42), []metrics.Label{{"my_tag", "my_value"}, {"other_tag", "other_value"}}, HostnameDisabled, "foo.bar:42.000000|g|#my_tag:my_value,other_tag:other_value"}, + {"SetGauge", []string{"foo", "bar"}, float32(42), []metrics.Label{{"my_tag", "my_value"}, {"other_tag", "other_value"}}, HostnameEnabled, "foo.bar:42.000000|g|#my_tag:my_value,other_tag:other_value,host:test_hostname"}, } -func mockNewDogStatsdSink(addr string, tags []string, tagWithHostname bool) *DogStatsdSink { +func mockNewDogStatsdSink(addr string, labels []metrics.Label, tagWithHostname bool) *DogStatsdSink { dog, _ := NewDogStatsdSink(addr, MockGetHostname()) + _, tags := dog.getFlatkeyAndCombinedLabels(nil, labels) dog.SetTags(tags) if tagWithHostname { dog.EnableHostNamePropagation() @@ -90,7 +93,7 @@ func TestParseKey(t *testing.T) { } if !reflect.DeepEqual(tags, tt.ExpectedTags) { - t.Fatalf("Tag Parsing Failed for %v", tt.KeyToParse) + t.Fatalf("Tag Parsing Failed for %v, %v != %v", tt.KeyToParse, tags, tt.ExpectedTags) } } } @@ -124,17 +127,17 @@ func TestTaggableMetrics(t *testing.T) { dog := mockNewDogStatsdSink(DogStatsdAddr, EmptyTags, HostnameDisabled) - dog.AddSampleWithTags([]string{"sample", "thing"}, float32(4), []string{"tagkey:tagvalue"}) + dog.AddSampleWithLabels([]string{"sample", "thing"}, float32(4), []metrics.Label{{"tagkey", "tagvalue"}}) assertServerMatchesExpected(t, server, buf, "sample.thing:4.000000|ms|#tagkey:tagvalue") - dog.SetGaugeWithTags([]string{"sample", "thing"}, float32(4), []string{"tagkey:tagvalue"}) + dog.SetGaugeWithLabels([]string{"sample", "thing"}, float32(4), []metrics.Label{{"tagkey", "tagvalue"}}) assertServerMatchesExpected(t, server, buf, "sample.thing:4.000000|g|#tagkey:tagvalue") - dog.IncrCounterWithTags([]string{"sample", "thing"}, float32(4), []string{"tagkey:tagvalue"}) + dog.IncrCounterWithLabels([]string{"sample", "thing"}, float32(4), []metrics.Label{{"tagkey", "tagvalue"}}) assertServerMatchesExpected(t, server, buf, "sample.thing:4|c|#tagkey:tagvalue") - dog = mockNewDogStatsdSink(DogStatsdAddr, []string{"global"}, HostnameEnabled) // with hostname, global tags - dog.IncrCounterWithTags([]string{"sample", "thing"}, float32(4), []string{"tagkey:tagvalue"}) + dog = mockNewDogStatsdSink(DogStatsdAddr, []metrics.Label{{Name: "global"}}, HostnameEnabled) // with hostname, global tags + dog.IncrCounterWithLabels([]string{"sample", "thing"}, float32(4), []metrics.Label{{"tagkey", "tagvalue"}}) assertServerMatchesExpected(t, server, buf, "sample.thing:4|c|#global,tagkey:tagvalue,host:test_hostname") } diff --git a/vendor/github.com/armon/go-metrics/inmem.go b/vendor/github.com/armon/go-metrics/inmem.go index ac46443be..cd1773042 100644 --- a/vendor/github.com/armon/go-metrics/inmem.go +++ b/vendor/github.com/armon/go-metrics/inmem.go @@ -1,6 +1,7 @@ package metrics import ( + "bytes" "fmt" "math" "net/url" @@ -39,7 +40,7 @@ type IntervalMetrics struct { Interval time.Time // Gauges maps the key to the last set value - Gauges map[string]float32 + Gauges map[string]GaugeValue // Points maps the string to the list of emitted values // from EmitKey @@ -47,21 +48,21 @@ type IntervalMetrics struct { // Counters maps the string key to a sum of the counter // values - Counters map[string]*AggregateSample + Counters map[string]SampledValue // Samples maps the key to an AggregateSample, // which has the rolled up view of a sample - Samples map[string]*AggregateSample + Samples map[string]SampledValue } // NewIntervalMetrics creates a new IntervalMetrics for a given interval func NewIntervalMetrics(intv time.Time) *IntervalMetrics { return &IntervalMetrics{ Interval: intv, - Gauges: make(map[string]float32), + Gauges: make(map[string]GaugeValue), Points: make(map[string][]float32), - Counters: make(map[string]*AggregateSample), - Samples: make(map[string]*AggregateSample), + Counters: make(map[string]SampledValue), + Samples: make(map[string]SampledValue), } } @@ -69,12 +70,12 @@ func NewIntervalMetrics(intv time.Time) *IntervalMetrics { // about a sample type AggregateSample struct { Count int // The count of emitted pairs - Rate float64 // The count of emitted pairs per time unit (usually 1 second) + Rate float64 `json:"-"` // The count of emitted pairs per time unit (usually 1 second) Sum float64 // The sum of values - SumSq float64 // The sum of squared values + SumSq float64 `json:"-"` // The sum of squared values Min float64 // Minimum value Max float64 // Maximum value - LastUpdated time.Time // When value was last updated + LastUpdated time.Time `json:"-"` // When value was last updated } // Computes a Stddev of the values @@ -154,12 +155,16 @@ func NewInmemSink(interval, retain time.Duration) *InmemSink { } func (i *InmemSink) SetGauge(key []string, val float32) { - k := i.flattenKey(key) + i.SetGaugeWithLabels(key, val, nil) +} + +func (i *InmemSink) SetGaugeWithLabels(key []string, val float32, labels []Label) { + k, name := i.flattenKeyLabels(key, labels) intv := i.getInterval() intv.Lock() defer intv.Unlock() - intv.Gauges[k] = val + intv.Gauges[k] = GaugeValue{Name: name, Value: val, Labels: labels} } func (i *InmemSink) EmitKey(key []string, val float32) { @@ -173,30 +178,46 @@ func (i *InmemSink) EmitKey(key []string, val float32) { } func (i *InmemSink) IncrCounter(key []string, val float32) { - k := i.flattenKey(key) + i.IncrCounterWithLabels(key, val, nil) +} + +func (i *InmemSink) IncrCounterWithLabels(key []string, val float32, labels []Label) { + k, name := i.flattenKeyLabels(key, labels) intv := i.getInterval() intv.Lock() defer intv.Unlock() - agg := intv.Counters[k] - if agg == nil { - agg = &AggregateSample{} + agg, ok := intv.Counters[k] + if !ok { + agg = SampledValue{ + Name: name, + AggregateSample: &AggregateSample{}, + Labels: labels, + } intv.Counters[k] = agg } agg.Ingest(float64(val), i.rateDenom) } func (i *InmemSink) AddSample(key []string, val float32) { - k := i.flattenKey(key) + i.AddSampleWithLabels(key, val, nil) +} + +func (i *InmemSink) AddSampleWithLabels(key []string, val float32, labels []Label) { + k, name := i.flattenKeyLabels(key, labels) intv := i.getInterval() intv.Lock() defer intv.Unlock() - agg := intv.Samples[k] - if agg == nil { - agg = &AggregateSample{} + agg, ok := intv.Samples[k] + if !ok { + agg = SampledValue{ + Name: name, + AggregateSample: &AggregateSample{}, + Labels: labels, + } intv.Samples[k] = agg } agg.Ingest(float64(val), i.rateDenom) @@ -261,6 +282,38 @@ func (i *InmemSink) getInterval() *IntervalMetrics { // Flattens the key for formatting, removes spaces func (i *InmemSink) flattenKey(parts []string) string { - joined := strings.Join(parts, ".") - return strings.Replace(joined, " ", "_", -1) + buf := &bytes.Buffer{} + replacer := strings.NewReplacer(" ", "_") + + if len(parts) > 0 { + replacer.WriteString(buf, parts[0]) + } + for _, part := range parts[1:] { + replacer.WriteString(buf, ".") + replacer.WriteString(buf, part) + } + + return buf.String() +} + +// Flattens the key for formatting along with its labels, removes spaces +func (i *InmemSink) flattenKeyLabels(parts []string, labels []Label) (string, string) { + buf := &bytes.Buffer{} + replacer := strings.NewReplacer(" ", "_") + + if len(parts) > 0 { + replacer.WriteString(buf, parts[0]) + } + for _, part := range parts[1:] { + replacer.WriteString(buf, ".") + replacer.WriteString(buf, part) + } + + key := buf.String() + + for _, label := range labels { + replacer.WriteString(buf, fmt.Sprintf(";%s=%s", label.Name, label.Value)) + } + + return buf.String(), key } diff --git a/vendor/github.com/armon/go-metrics/inmem_endpoint.go b/vendor/github.com/armon/go-metrics/inmem_endpoint.go new file mode 100644 index 000000000..504f1b374 --- /dev/null +++ b/vendor/github.com/armon/go-metrics/inmem_endpoint.go @@ -0,0 +1,118 @@ +package metrics + +import ( + "fmt" + "net/http" + "sort" + "time" +) + +// MetricsSummary holds a roll-up of metrics info for a given interval +type MetricsSummary struct { + Timestamp string + Gauges []GaugeValue + Points []PointValue + Counters []SampledValue + Samples []SampledValue +} + +type GaugeValue struct { + Name string + Hash string `json:"-"` + Value float32 + + Labels []Label `json:"-"` + DisplayLabels map[string]string `json:"Labels"` +} + +type PointValue struct { + Name string + Points []float32 +} + +type SampledValue struct { + Name string + Hash string `json:"-"` + *AggregateSample + Mean float64 + Stddev float64 + + Labels []Label `json:"-"` + DisplayLabels map[string]string `json:"Labels"` +} + +// DisplayMetrics returns a summary of the metrics from the most recent finished interval. +func (i *InmemSink) DisplayMetrics(resp http.ResponseWriter, req *http.Request) (interface{}, error) { + data := i.Data() + + var interval *IntervalMetrics + n := len(data) + switch { + case n == 0: + return nil, fmt.Errorf("no metric intervals have been initialized yet") + case n == 1: + // Show the current interval if it's all we have + interval = i.intervals[0] + default: + // Show the most recent finished interval if we have one + interval = i.intervals[n-2] + } + + summary := MetricsSummary{ + Timestamp: interval.Interval.Round(time.Second).UTC().String(), + Gauges: make([]GaugeValue, 0, len(interval.Gauges)), + Points: make([]PointValue, 0, len(interval.Points)), + } + + // Format and sort the output of each metric type, so it gets displayed in a + // deterministic order. + for name, points := range interval.Points { + summary.Points = append(summary.Points, PointValue{name, points}) + } + sort.Slice(summary.Points, func(i, j int) bool { + return summary.Points[i].Name < summary.Points[j].Name + }) + + for hash, value := range interval.Gauges { + value.Hash = hash + value.DisplayLabels = make(map[string]string) + for _, label := range value.Labels { + value.DisplayLabels[label.Name] = label.Value + } + value.Labels = nil + + summary.Gauges = append(summary.Gauges, value) + } + sort.Slice(summary.Gauges, func(i, j int) bool { + return summary.Gauges[i].Hash < summary.Gauges[j].Hash + }) + + summary.Counters = formatSamples(interval.Counters) + summary.Samples = formatSamples(interval.Samples) + + return summary, nil +} + +func formatSamples(source map[string]SampledValue) []SampledValue { + output := make([]SampledValue, 0, len(source)) + for hash, sample := range source { + displayLabels := make(map[string]string) + for _, label := range sample.Labels { + displayLabels[label.Name] = label.Value + } + + output = append(output, SampledValue{ + Name: sample.Name, + Hash: hash, + AggregateSample: sample.AggregateSample, + Mean: sample.AggregateSample.Mean(), + Stddev: sample.AggregateSample.Stddev(), + DisplayLabels: displayLabels, + }) + } + sort.Slice(output, func(i, j int) bool { + return output[i].Hash < output[j].Hash + }) + + return output +} diff --git a/vendor/github.com/armon/go-metrics/inmem_endpoint_test.go b/vendor/github.com/armon/go-metrics/inmem_endpoint_test.go new file mode 100644 index 000000000..326d56ada --- /dev/null +++ b/vendor/github.com/armon/go-metrics/inmem_endpoint_test.go @@ -0,0 +1,133 @@ +package metrics + +import ( + "testing" + "time" + + "github.com/pascaldekloe/goe/verify" +) + +func TestDisplayMetrics(t *testing.T) { + interval := 10 * time.Millisecond + inm := NewInmemSink(interval, 50*time.Millisecond) + + // Add data points + inm.SetGauge([]string{"foo", "bar"}, 42) + inm.SetGaugeWithLabels([]string{"foo", "bar"}, 23, []Label{{"a", "b"}}) + inm.EmitKey([]string{"foo", "bar"}, 42) + inm.IncrCounter([]string{"foo", "bar"}, 20) + inm.IncrCounter([]string{"foo", "bar"}, 22) + inm.IncrCounterWithLabels([]string{"foo", "bar"}, 20, []Label{{"a", "b"}}) + inm.IncrCounterWithLabels([]string{"foo", "bar"}, 40, []Label{{"a", "b"}}) + inm.AddSample([]string{"foo", "bar"}, 20) + inm.AddSample([]string{"foo", "bar"}, 24) + inm.AddSampleWithLabels([]string{"foo", "bar"}, 23, []Label{{"a", "b"}}) + inm.AddSampleWithLabels([]string{"foo", "bar"}, 33, []Label{{"a", "b"}}) + + data := inm.Data() + if len(data) != 1 { + t.Fatalf("bad: %v", data) + } + + expected := MetricsSummary{ + Timestamp: data[0].Interval.Round(time.Second).UTC().String(), + Gauges: []GaugeValue{ + { + Name: "foo.bar", + Hash: "foo.bar", + Value: float32(42), + DisplayLabels: map[string]string{}, + }, + { + Name: "foo.bar", + Hash: "foo.bar;a=b", + Value: float32(23), + DisplayLabels: map[string]string{"a": "b"}, + }, + }, + Points: []PointValue{ + { + Name: "foo.bar", + Points: []float32{42}, + }, + }, + Counters: []SampledValue{ + { + Name: "foo.bar", + Hash: "foo.bar", + AggregateSample: &AggregateSample{ + Count: 2, + Min: 20, + Max: 22, + Sum: 42, + SumSq: 884, + Rate: 200, + }, + Mean: 21, + Stddev: 1.4142135623730951, + }, + { + Name: "foo.bar", + Hash: "foo.bar;a=b", + AggregateSample: &AggregateSample{ + Count: 2, + Min: 20, + Max: 40, + Sum: 60, + SumSq: 2000, + Rate: 200, + }, + Mean: 30, + Stddev: 14.142135623730951, + DisplayLabels: map[string]string{"a": "b"}, + }, + }, + Samples: []SampledValue{ + { + Name: "foo.bar", + Hash: "foo.bar", + AggregateSample: &AggregateSample{ + Count: 2, + Min: 20, + Max: 24, + Sum: 44, + SumSq: 976, + Rate: 200, + }, + Mean: 22, + Stddev: 2.8284271247461903, + }, + { + Name: "foo.bar", + Hash: "foo.bar;a=b", + AggregateSample: &AggregateSample{ + Count: 2, + Min: 23, + Max: 33, + Sum: 56, + SumSq: 1618, + Rate: 200, + }, + Mean: 28, + Stddev: 7.0710678118654755, + DisplayLabels: map[string]string{"a": "b"}, + }, + }, + } + + raw, err := inm.DisplayMetrics(nil, nil) + if err != nil { + t.Fatalf("err: %v", err) + } + result := raw.(MetricsSummary) + + // Ignore the LastUpdated field, we don't export that anyway + for i, got := range result.Counters { + expected.Counters[i].LastUpdated = got.LastUpdated + } + for i, got := range result.Samples { + expected.Samples[i].LastUpdated = got.LastUpdated + } + + verify.Values(t, "all", result, expected) +} diff --git a/vendor/github.com/armon/go-metrics/inmem_signal.go b/vendor/github.com/armon/go-metrics/inmem_signal.go index 95d08ee10..0937f4aed 100644 --- a/vendor/github.com/armon/go-metrics/inmem_signal.go +++ b/vendor/github.com/armon/go-metrics/inmem_signal.go @@ -6,6 +6,7 @@ import ( "io" "os" "os/signal" + "strings" "sync" "syscall" ) @@ -75,22 +76,25 @@ func (i *InmemSignal) dumpStats() { data := i.inm.Data() // Skip the last period which is still being aggregated - for i := 0; i < len(data)-1; i++ { - intv := data[i] + for j := 0; j < len(data)-1; j++ { + intv := data[j] intv.RLock() - for name, val := range intv.Gauges { - fmt.Fprintf(buf, "[%v][G] '%s': %0.3f\n", intv.Interval, name, val) + for _, val := range intv.Gauges { + name := i.flattenLabels(val.Name, val.Labels) + fmt.Fprintf(buf, "[%v][G] '%s': %0.3f\n", intv.Interval, name, val.Value) } for name, vals := range intv.Points { for _, val := range vals { fmt.Fprintf(buf, "[%v][P] '%s': %0.3f\n", intv.Interval, name, val) } } - for name, agg := range intv.Counters { - fmt.Fprintf(buf, "[%v][C] '%s': %s\n", intv.Interval, name, agg) + for _, agg := range intv.Counters { + name := i.flattenLabels(agg.Name, agg.Labels) + fmt.Fprintf(buf, "[%v][C] '%s': %s\n", intv.Interval, name, agg.AggregateSample) } - for name, agg := range intv.Samples { - fmt.Fprintf(buf, "[%v][S] '%s': %s\n", intv.Interval, name, agg) + for _, agg := range intv.Samples { + name := i.flattenLabels(agg.Name, agg.Labels) + fmt.Fprintf(buf, "[%v][S] '%s': %s\n", intv.Interval, name, agg.AggregateSample) } intv.RUnlock() } @@ -98,3 +102,16 @@ func (i *InmemSignal) dumpStats() { // Write out the bytes i.w.Write(buf.Bytes()) } + +// Flattens the key for formatting along with its labels, removes spaces +func (i *InmemSignal) flattenLabels(name string, labels []Label) string { + buf := bytes.NewBufferString(name) + replacer := strings.NewReplacer(" ", "_", ":", "_") + + for _, label := range labels { + replacer.WriteString(buf, ".") + replacer.WriteString(buf, label.Value) + } + + return buf.String() +} diff --git a/vendor/github.com/armon/go-metrics/inmem_signal_test.go b/vendor/github.com/armon/go-metrics/inmem_signal_test.go index 9bbca5f25..c992d6bce 100644 --- a/vendor/github.com/armon/go-metrics/inmem_signal_test.go +++ b/vendor/github.com/armon/go-metrics/inmem_signal_test.go @@ -19,6 +19,9 @@ func TestInmemSignal(t *testing.T) { inm.EmitKey([]string{"bar"}, 42) inm.IncrCounter([]string{"baz"}, 42) inm.AddSample([]string{"wow"}, 42) + inm.SetGaugeWithLabels([]string{"asdf"}, 42, []Label{{"a", "b"}}) + inm.IncrCounterWithLabels([]string{"qwer"}, 42, []Label{{"a", "b"}}) + inm.AddSampleWithLabels([]string{"zxcv"}, 42, []Label{{"a", "b"}}) // Wait for period to end time.Sleep(15 * time.Millisecond) @@ -43,4 +46,13 @@ func TestInmemSignal(t *testing.T) { if !strings.Contains(out, "[S] 'wow': Count: 1 Sum: 42") { t.Fatalf("bad: %v", out) } + if !strings.Contains(out, "[G] 'asdf.b': 42") { + t.Fatalf("bad: %v", out) + } + if !strings.Contains(out, "[C] 'qwer.b': Count: 1 Sum: 42") { + t.Fatalf("bad: %v", out) + } + if !strings.Contains(out, "[S] 'zxcv.b': Count: 1 Sum: 42") { + t.Fatalf("bad: %v", out) + } } diff --git a/vendor/github.com/armon/go-metrics/inmem_test.go b/vendor/github.com/armon/go-metrics/inmem_test.go index ed3b521da..8d30e7c30 100644 --- a/vendor/github.com/armon/go-metrics/inmem_test.go +++ b/vendor/github.com/armon/go-metrics/inmem_test.go @@ -18,11 +18,15 @@ func TestInmemSink(t *testing.T) { // Add data points inm.SetGauge([]string{"foo", "bar"}, 42) + inm.SetGaugeWithLabels([]string{"foo", "bar"}, 23, []Label{{"a", "b"}}) inm.EmitKey([]string{"foo", "bar"}, 42) inm.IncrCounter([]string{"foo", "bar"}, 20) inm.IncrCounter([]string{"foo", "bar"}, 22) + inm.IncrCounterWithLabels([]string{"foo", "bar"}, 20, []Label{{"a", "b"}}) + inm.IncrCounterWithLabels([]string{"foo", "bar"}, 22, []Label{{"a", "b"}}) inm.AddSample([]string{"foo", "bar"}, 20) inm.AddSample([]string{"foo", "bar"}, 22) + inm.AddSampleWithLabels([]string{"foo", "bar"}, 23, []Label{{"a", "b"}}) data = inm.Data() if len(data) != 1 { @@ -35,49 +39,57 @@ func TestInmemSink(t *testing.T) { if time.Now().Sub(intvM.Interval) > 10*time.Millisecond { t.Fatalf("interval too old") } - if intvM.Gauges["foo.bar"] != 42 { + if intvM.Gauges["foo.bar"].Value != 42 { + t.Fatalf("bad val: %v", intvM.Gauges) + } + if intvM.Gauges["foo.bar;a=b"].Value != 23 { t.Fatalf("bad val: %v", intvM.Gauges) } if intvM.Points["foo.bar"][0] != 42 { t.Fatalf("bad val: %v", intvM.Points) } - agg := intvM.Counters["foo.bar"] - if agg.Count != 2 { - t.Fatalf("bad val: %v", agg) - } - if agg.Rate != 200 { - t.Fatalf("bad val: %v", agg.Rate) - } - if agg.Sum != 42 { - t.Fatalf("bad val: %v", agg) - } - if agg.SumSq != 884 { - t.Fatalf("bad val: %v", agg) - } - if agg.Min != 20 { - t.Fatalf("bad val: %v", agg) - } - if agg.Max != 22 { - t.Fatalf("bad val: %v", agg) - } - if agg.Mean() != 21 { - t.Fatalf("bad val: %v", agg) - } - if agg.Stddev() != math.Sqrt(2) { - t.Fatalf("bad val: %v", agg) - } + for _, agg := range []SampledValue{intvM.Counters["foo.bar"], intvM.Counters["foo.bar;a=b"]} { + if agg.Count != 2 { + t.Fatalf("bad val: %v", agg) + } + if agg.Rate != 200 { + t.Fatalf("bad val: %v", agg.Rate) + } + if agg.Sum != 42 { + t.Fatalf("bad val: %v", agg) + } + if agg.SumSq != 884 { + t.Fatalf("bad val: %v", agg) + } + if agg.Min != 20 { + t.Fatalf("bad val: %v", agg) + } + if agg.Max != 22 { + t.Fatalf("bad val: %v", agg) + } + if agg.AggregateSample.Mean() != 21 { + t.Fatalf("bad val: %v", agg) + } + if agg.AggregateSample.Stddev() != math.Sqrt(2) { + t.Fatalf("bad val: %v", agg) + } + + if agg.LastUpdated.IsZero() { + t.Fatalf("agg.LastUpdated is not set: %v", agg) + } - if agg.LastUpdated.IsZero() { - t.Fatalf("agg.LastUpdated is not set: %v", agg) + diff := time.Now().Sub(agg.LastUpdated).Seconds() + if diff > 1 { + t.Fatalf("time diff too great: %f", diff) + } } - diff := time.Now().Sub(agg.LastUpdated).Seconds() - if diff > 1 { - t.Fatalf("time diff too great: %f", diff) + if _, ok := intvM.Samples["foo.bar"]; !ok { + t.Fatalf("missing sample") } - if agg = intvM.Samples["foo.bar"]; agg == nil { + if _, ok := intvM.Samples["foo.bar;a=b"]; !ok { t.Fatalf("missing sample") } diff --git a/vendor/github.com/armon/go-metrics/metrics.go b/vendor/github.com/armon/go-metrics/metrics.go index b818e4182..d260bd4b2 100644 --- a/vendor/github.com/armon/go-metrics/metrics.go +++ b/vendor/github.com/armon/go-metrics/metrics.go @@ -2,20 +2,43 @@ package metrics import ( "runtime" + "strings" "time" + + "github.com/hashicorp/go-immutable-radix" ) +type Label struct { + Name string + Value string +} + func (m *Metrics) SetGauge(key []string, val float32) { - if m.HostName != "" && m.EnableHostname { - key = insert(0, m.HostName, key) + m.SetGaugeWithLabels(key, val, nil) +} + +func (m *Metrics) SetGaugeWithLabels(key []string, val float32, labels []Label) { + if m.HostName != "" { + if m.EnableHostnameLabel { + labels = append(labels, Label{"host", m.HostName}) + } else if m.EnableHostname { + key = insert(0, m.HostName, key) + } } if m.EnableTypePrefix { key = insert(0, "gauge", key) } if m.ServiceName != "" { - key = insert(0, m.ServiceName, key) + if m.EnableServiceLabel { + labels = append(labels, Label{"service", m.ServiceName}) + } else { + key = insert(0, m.ServiceName, key) + } + } + if !m.allowMetric(key) { + return } - m.sink.SetGauge(key, val) + m.sink.SetGaugeWithLabels(key, val, labels) } func (m *Metrics) EmitKey(key []string, val float32) { @@ -25,40 +48,118 @@ func (m *Metrics) EmitKey(key []string, val float32) { if m.ServiceName != "" { key = insert(0, m.ServiceName, key) } + if !m.allowMetric(key) { + return + } m.sink.EmitKey(key, val) } func (m *Metrics) IncrCounter(key []string, val float32) { + m.IncrCounterWithLabels(key, val, nil) +} + +func (m *Metrics) IncrCounterWithLabels(key []string, val float32, labels []Label) { + if m.HostName != "" && m.EnableHostnameLabel { + labels = append(labels, Label{"host", m.HostName}) + } if m.EnableTypePrefix { key = insert(0, "counter", key) } if m.ServiceName != "" { - key = insert(0, m.ServiceName, key) + if m.EnableServiceLabel { + labels = append(labels, Label{"service", m.ServiceName}) + } else { + key = insert(0, m.ServiceName, key) + } } - m.sink.IncrCounter(key, val) + if !m.allowMetric(key) { + return + } + m.sink.IncrCounterWithLabels(key, val, labels) } func (m *Metrics) AddSample(key []string, val float32) { + m.AddSampleWithLabels(key, val, nil) +} + +func (m *Metrics) AddSampleWithLabels(key []string, val float32, labels []Label) { + if m.HostName != "" && m.EnableHostnameLabel { + labels = append(labels, Label{"host", m.HostName}) + } if m.EnableTypePrefix { key = insert(0, "sample", key) } if m.ServiceName != "" { - key = insert(0, m.ServiceName, key) + if m.EnableServiceLabel { + labels = append(labels, Label{"service", m.ServiceName}) + } else { + key = insert(0, m.ServiceName, key) + } + } + if !m.allowMetric(key) { + return } - m.sink.AddSample(key, val) + m.sink.AddSampleWithLabels(key, val, labels) } func (m *Metrics) MeasureSince(key []string, start time.Time) { + m.MeasureSinceWithLabels(key, start, nil) +} + +func (m *Metrics) MeasureSinceWithLabels(key []string, start time.Time, labels []Label) { + if m.HostName != "" && m.EnableHostnameLabel { + labels = append(labels, Label{"host", m.HostName}) + } if m.EnableTypePrefix { key = insert(0, "timer", key) } if m.ServiceName != "" { - key = insert(0, m.ServiceName, key) + if m.EnableServiceLabel { + labels = append(labels, Label{"service", m.ServiceName}) + } else { + key = insert(0, m.ServiceName, key) + } + } + if !m.allowMetric(key) { + return } now := time.Now() elapsed := now.Sub(start) msec := float32(elapsed.Nanoseconds()) / float32(m.TimerGranularity) - m.sink.AddSample(key, msec) + m.sink.AddSampleWithLabels(key, msec, labels) +} + +// UpdateFilter overwrites the existing filter with the given rules. +func (m *Metrics) UpdateFilter(allow, block []string) { + m.filterLock.Lock() + defer m.filterLock.Unlock() + + m.AllowedPrefixes = allow + m.BlockedPrefixes = block + + m.filter = iradix.New() + for _, prefix := range m.AllowedPrefixes { + m.filter, _, _ = m.filter.Insert([]byte(prefix), true) + } + for _, prefix := range m.BlockedPrefixes { + m.filter, _, _ = m.filter.Insert([]byte(prefix), false) + } +} + +// Returns whether the metric should be allowed based on configured prefix filters +func (m *Metrics) allowMetric(key []string) bool { + m.filterLock.RLock() + defer m.filterLock.RUnlock() + + if m.filter == nil || m.filter.Len() == 0 { + return m.Config.FilterDefault + } + + _, allowed, ok := m.filter.Root().LongestPrefix([]byte(strings.Join(key, "."))) + if !ok { + return m.Config.FilterDefault + } + return allowed.(bool) } // Periodically collects runtime stats to publish diff --git a/vendor/github.com/armon/go-metrics/metrics_test.go b/vendor/github.com/armon/go-metrics/metrics_test.go index f5b2a4c79..8556a0019 100644 --- a/vendor/github.com/armon/go-metrics/metrics_test.go +++ b/vendor/github.com/armon/go-metrics/metrics_test.go @@ -9,7 +9,7 @@ import ( func mockMetric() (*MockSink, *Metrics) { m := &MockSink{} - met := &Metrics{sink: m} + met := &Metrics{Config: Config{FilterDefault: true}, sink: m} return m, met } @@ -23,6 +23,19 @@ func TestMetrics_SetGauge(t *testing.T) { t.Fatalf("") } + m, met = mockMetric() + labels := []Label{{"a", "b"}} + met.SetGaugeWithLabels([]string{"key"}, float32(1), labels) + if m.keys[0][0] != "key" { + t.Fatalf("") + } + if m.vals[0] != 1 { + t.Fatalf("") + } + if !reflect.DeepEqual(m.labels[0], labels) { + t.Fatalf("") + } + m, met = mockMetric() met.HostName = "test" met.EnableHostname = true @@ -96,6 +109,19 @@ func TestMetrics_IncrCounter(t *testing.T) { t.Fatalf("") } + m, met = mockMetric() + labels := []Label{{"a", "b"}} + met.IncrCounterWithLabels([]string{"key"}, float32(1), labels) + if m.keys[0][0] != "key" { + t.Fatalf("") + } + if m.vals[0] != 1 { + t.Fatalf("") + } + if !reflect.DeepEqual(m.labels[0], labels) { + t.Fatalf("") + } + m, met = mockMetric() met.EnableTypePrefix = true met.IncrCounter([]string{"key"}, float32(1)) @@ -127,6 +153,19 @@ func TestMetrics_AddSample(t *testing.T) { t.Fatalf("") } + m, met = mockMetric() + labels := []Label{{"a", "b"}} + met.AddSampleWithLabels([]string{"key"}, float32(1), labels) + if m.keys[0][0] != "key" { + t.Fatalf("") + } + if m.vals[0] != 1 { + t.Fatalf("") + } + if !reflect.DeepEqual(m.labels[0], labels) { + t.Fatalf("") + } + m, met = mockMetric() met.EnableTypePrefix = true met.AddSample([]string{"key"}, float32(1)) @@ -160,6 +199,20 @@ func TestMetrics_MeasureSince(t *testing.T) { t.Fatalf("") } + m, met = mockMetric() + met.TimerGranularity = time.Millisecond + labels := []Label{{"a", "b"}} + met.MeasureSinceWithLabels([]string{"key"}, n, labels) + if m.keys[0][0] != "key" { + t.Fatalf("") + } + if m.vals[0] > 0.1 { + t.Fatalf("") + } + if !reflect.DeepEqual(m.labels[0], labels) { + t.Fatalf("") + } + m, met = mockMetric() met.TimerGranularity = time.Millisecond met.EnableTypePrefix = true @@ -260,3 +313,99 @@ func TestInsert(t *testing.T) { t.Fatalf("bad insert %v %v", exp, out) } } + +func TestMetrics_Filter_Blacklist(t *testing.T) { + m := &MockSink{} + conf := DefaultConfig("") + conf.AllowedPrefixes = []string{"service", "debug.thing"} + conf.BlockedPrefixes = []string{"debug"} + conf.EnableHostname = false + met, err := New(conf, m) + if err != nil { + t.Fatal(err) + } + + // Allowed by default + key := []string{"thing"} + met.SetGauge(key, 1) + if !reflect.DeepEqual(m.keys[0], key) { + t.Fatalf("key doesn't exist %v, %v", m.keys[0], key) + } + if m.vals[0] != 1 { + t.Fatalf("bad val: %v", m.vals[0]) + } + + // Allowed by filter + key = []string{"service", "thing"} + met.SetGauge(key, 2) + if !reflect.DeepEqual(m.keys[1], key) { + t.Fatalf("key doesn't exist") + } + if m.vals[1] != 2 { + t.Fatalf("bad val: %v", m.vals[1]) + } + + // Allowed by filter, subtree of a blocked entry + key = []string{"debug", "thing"} + met.SetGauge(key, 3) + if !reflect.DeepEqual(m.keys[2], key) { + t.Fatalf("key doesn't exist") + } + if m.vals[2] != 3 { + t.Fatalf("bad val: %v", m.vals[2]) + } + + // Blocked by filter + key = []string{"debug", "other-thing"} + met.SetGauge(key, 4) + if len(m.keys) != 3 { + t.Fatalf("key shouldn't exist") + } +} + +func TestMetrics_Filter_Whitelist(t *testing.T) { + m := &MockSink{} + conf := DefaultConfig("") + conf.AllowedPrefixes = []string{"service", "debug.thing"} + conf.BlockedPrefixes = []string{"debug"} + conf.FilterDefault = false + conf.EnableHostname = false + met, err := New(conf, m) + if err != nil { + t.Fatal(err) + } + + // Blocked by default + key := []string{"thing"} + met.SetGauge(key, 1) + if len(m.keys) != 0 { + t.Fatalf("key should not exist") + } + + // Allowed by filter + key = []string{"service", "thing"} + met.SetGauge(key, 2) + if !reflect.DeepEqual(m.keys[0], key) { + t.Fatalf("key doesn't exist") + } + if m.vals[0] != 2 { + t.Fatalf("bad val: %v", m.vals[0]) + } + + // Allowed by filter, subtree of a blocked entry + key = []string{"debug", "thing"} + met.SetGauge(key, 3) + if !reflect.DeepEqual(m.keys[1], key) { + t.Fatalf("key doesn't exist") + } + if m.vals[1] != 3 { + t.Fatalf("bad val: %v", m.vals[1]) + } + + // Blocked by filter + key = []string{"debug", "other-thing"} + met.SetGauge(key, 4) + if len(m.keys) != 2 { + t.Fatalf("key shouldn't exist") + } +} diff --git a/vendor/github.com/armon/go-metrics/prometheus/prometheus.go b/vendor/github.com/armon/go-metrics/prometheus/prometheus.go index b26d27981..f31f2f9a5 100644 --- a/vendor/github.com/armon/go-metrics/prometheus/prometheus.go +++ b/vendor/github.com/armon/go-metrics/prometheus/prometheus.go @@ -2,10 +2,14 @@ package prometheus import ( + "fmt" "strings" "sync" "time" + "regexp" + + "github.com/armon/go-metrics" "github.com/prometheus/client_golang/prometheus" ) @@ -24,24 +28,42 @@ func NewPrometheusSink() (*PrometheusSink, error) { }, nil } -func (p *PrometheusSink) flattenKey(parts []string) string { - joined := strings.Join(parts, "_") - joined = strings.Replace(joined, " ", "_", -1) - joined = strings.Replace(joined, ".", "_", -1) - joined = strings.Replace(joined, "-", "_", -1) - joined = strings.Replace(joined, "=", "_", -1) - return joined +var forbiddenChars = regexp.MustCompile("[ .=\\-]") + +func (p *PrometheusSink) flattenKey(parts []string, labels []metrics.Label) (string, string) { + key := strings.Join(parts, "_") + key = forbiddenChars.ReplaceAllString(key, "_") + + hash := key + for _, label := range labels { + hash += fmt.Sprintf(";%s=%s", label.Name, label.Value) + } + + return key, hash +} + +func prometheusLabels(labels []metrics.Label) prometheus.Labels { + l := make(prometheus.Labels) + for _, label := range labels { + l[label.Name] = label.Value + } + return l } func (p *PrometheusSink) SetGauge(parts []string, val float32) { + p.SetGaugeWithLabels(parts, val, nil) +} + +func (p *PrometheusSink) SetGaugeWithLabels(parts []string, val float32, labels []metrics.Label) { p.mu.Lock() defer p.mu.Unlock() - key := p.flattenKey(parts) - g, ok := p.gauges[key] + key, hash := p.flattenKey(parts, labels) + g, ok := p.gauges[hash] if !ok { g = prometheus.NewGauge(prometheus.GaugeOpts{ - Name: key, - Help: key, + Name: key, + Help: key, + ConstLabels: prometheusLabels(labels), }) prometheus.MustRegister(g) p.gauges[key] = g @@ -50,15 +72,20 @@ func (p *PrometheusSink) SetGauge(parts []string, val float32) { } func (p *PrometheusSink) AddSample(parts []string, val float32) { + p.AddSampleWithLabels(parts, val, nil) +} + +func (p *PrometheusSink) AddSampleWithLabels(parts []string, val float32, labels []metrics.Label) { p.mu.Lock() defer p.mu.Unlock() - key := p.flattenKey(parts) - g, ok := p.summaries[key] + key, hash := p.flattenKey(parts, labels) + g, ok := p.summaries[hash] if !ok { g = prometheus.NewSummary(prometheus.SummaryOpts{ - Name: key, - Help: key, - MaxAge: 10 * time.Second, + Name: key, + Help: key, + MaxAge: 10 * time.Second, + ConstLabels: prometheusLabels(labels), }) prometheus.MustRegister(g) p.summaries[key] = g @@ -73,14 +100,19 @@ func (p *PrometheusSink) EmitKey(key []string, val float32) { } func (p *PrometheusSink) IncrCounter(parts []string, val float32) { + p.IncrCounterWithLabels(parts, val, nil) +} + +func (p *PrometheusSink) IncrCounterWithLabels(parts []string, val float32, labels []metrics.Label) { p.mu.Lock() defer p.mu.Unlock() - key := p.flattenKey(parts) - g, ok := p.counters[key] + key, hash := p.flattenKey(parts, labels) + g, ok := p.counters[hash] if !ok { g = prometheus.NewCounter(prometheus.CounterOpts{ - Name: key, - Help: key, + Name: key, + Help: key, + ConstLabels: prometheusLabels(labels), }) prometheus.MustRegister(g) p.counters[key] = g diff --git a/vendor/github.com/armon/go-metrics/sink.go b/vendor/github.com/armon/go-metrics/sink.go index 9f7e2f6a2..0b7d6e4be 100644 --- a/vendor/github.com/armon/go-metrics/sink.go +++ b/vendor/github.com/armon/go-metrics/sink.go @@ -10,31 +10,41 @@ import ( type MetricSink interface { // A Gauge should retain the last value it is set to SetGauge(key []string, val float32) + SetGaugeWithLabels(key []string, val float32, labels []Label) // Should emit a Key/Value pair for each call EmitKey(key []string, val float32) // Counters should accumulate values IncrCounter(key []string, val float32) + IncrCounterWithLabels(key []string, val float32, labels []Label) // Samples are for timing information, where quantiles are used AddSample(key []string, val float32) + AddSampleWithLabels(key []string, val float32, labels []Label) } // BlackholeSink is used to just blackhole messages type BlackholeSink struct{} -func (*BlackholeSink) SetGauge(key []string, val float32) {} -func (*BlackholeSink) EmitKey(key []string, val float32) {} -func (*BlackholeSink) IncrCounter(key []string, val float32) {} -func (*BlackholeSink) AddSample(key []string, val float32) {} +func (*BlackholeSink) SetGauge(key []string, val float32) {} +func (*BlackholeSink) SetGaugeWithLabels(key []string, val float32, labels []Label) {} +func (*BlackholeSink) EmitKey(key []string, val float32) {} +func (*BlackholeSink) IncrCounter(key []string, val float32) {} +func (*BlackholeSink) IncrCounterWithLabels(key []string, val float32, labels []Label) {} +func (*BlackholeSink) AddSample(key []string, val float32) {} +func (*BlackholeSink) AddSampleWithLabels(key []string, val float32, labels []Label) {} // FanoutSink is used to sink to fanout values to multiple sinks type FanoutSink []MetricSink func (fh FanoutSink) SetGauge(key []string, val float32) { + fh.SetGaugeWithLabels(key, val, nil) +} + +func (fh FanoutSink) SetGaugeWithLabels(key []string, val float32, labels []Label) { for _, s := range fh { - s.SetGauge(key, val) + s.SetGaugeWithLabels(key, val, labels) } } @@ -45,14 +55,22 @@ func (fh FanoutSink) EmitKey(key []string, val float32) { } func (fh FanoutSink) IncrCounter(key []string, val float32) { + fh.IncrCounterWithLabels(key, val, nil) +} + +func (fh FanoutSink) IncrCounterWithLabels(key []string, val float32, labels []Label) { for _, s := range fh { - s.IncrCounter(key, val) + s.IncrCounterWithLabels(key, val, labels) } } func (fh FanoutSink) AddSample(key []string, val float32) { + fh.AddSampleWithLabels(key, val, nil) +} + +func (fh FanoutSink) AddSampleWithLabels(key []string, val float32, labels []Label) { for _, s := range fh { - s.AddSample(key, val) + s.AddSampleWithLabels(key, val, labels) } } diff --git a/vendor/github.com/armon/go-metrics/sink_test.go b/vendor/github.com/armon/go-metrics/sink_test.go index 77c5c3278..714f99b81 100644 --- a/vendor/github.com/armon/go-metrics/sink_test.go +++ b/vendor/github.com/armon/go-metrics/sink_test.go @@ -7,25 +7,39 @@ import ( ) type MockSink struct { - keys [][]string - vals []float32 + keys [][]string + vals []float32 + labels [][]Label } func (m *MockSink) SetGauge(key []string, val float32) { + m.SetGaugeWithLabels(key, val, nil) +} +func (m *MockSink) SetGaugeWithLabels(key []string, val float32, labels []Label) { m.keys = append(m.keys, key) m.vals = append(m.vals, val) + m.labels = append(m.labels, labels) } func (m *MockSink) EmitKey(key []string, val float32) { m.keys = append(m.keys, key) m.vals = append(m.vals, val) + m.labels = append(m.labels, nil) } func (m *MockSink) IncrCounter(key []string, val float32) { + m.IncrCounterWithLabels(key, val, nil) +} +func (m *MockSink) IncrCounterWithLabels(key []string, val float32, labels []Label) { m.keys = append(m.keys, key) m.vals = append(m.vals, val) + m.labels = append(m.labels, labels) } func (m *MockSink) AddSample(key []string, val float32) { + m.AddSampleWithLabels(key, val, nil) +} +func (m *MockSink) AddSampleWithLabels(key []string, val float32, labels []Label) { m.keys = append(m.keys, key) m.vals = append(m.vals, val) + m.labels = append(m.labels, labels) } func TestFanoutSink_Gauge(t *testing.T) { @@ -51,6 +65,36 @@ func TestFanoutSink_Gauge(t *testing.T) { } } +func TestFanoutSink_Gauge_Labels(t *testing.T) { + m1 := &MockSink{} + m2 := &MockSink{} + fh := &FanoutSink{m1, m2} + + k := []string{"test"} + v := float32(42.0) + l := []Label{{"a", "b"}} + fh.SetGaugeWithLabels(k, v, l) + + if !reflect.DeepEqual(m1.keys[0], k) { + t.Fatalf("key not equal") + } + if !reflect.DeepEqual(m2.keys[0], k) { + t.Fatalf("key not equal") + } + if !reflect.DeepEqual(m1.vals[0], v) { + t.Fatalf("val not equal") + } + if !reflect.DeepEqual(m2.vals[0], v) { + t.Fatalf("val not equal") + } + if !reflect.DeepEqual(m1.labels[0], l) { + t.Fatalf("labels not equal") + } + if !reflect.DeepEqual(m2.labels[0], l) { + t.Fatalf("labels not equal") + } +} + func TestFanoutSink_Key(t *testing.T) { m1 := &MockSink{} m2 := &MockSink{} @@ -97,6 +141,36 @@ func TestFanoutSink_Counter(t *testing.T) { } } +func TestFanoutSink_Counter_Labels(t *testing.T) { + m1 := &MockSink{} + m2 := &MockSink{} + fh := &FanoutSink{m1, m2} + + k := []string{"test"} + v := float32(42.0) + l := []Label{{"a", "b"}} + fh.IncrCounterWithLabels(k, v, l) + + if !reflect.DeepEqual(m1.keys[0], k) { + t.Fatalf("key not equal") + } + if !reflect.DeepEqual(m2.keys[0], k) { + t.Fatalf("key not equal") + } + if !reflect.DeepEqual(m1.vals[0], v) { + t.Fatalf("val not equal") + } + if !reflect.DeepEqual(m2.vals[0], v) { + t.Fatalf("val not equal") + } + if !reflect.DeepEqual(m1.labels[0], l) { + t.Fatalf("labels not equal") + } + if !reflect.DeepEqual(m2.labels[0], l) { + t.Fatalf("labels not equal") + } +} + func TestFanoutSink_Sample(t *testing.T) { m1 := &MockSink{} m2 := &MockSink{} @@ -120,6 +194,36 @@ func TestFanoutSink_Sample(t *testing.T) { } } +func TestFanoutSink_Sample_Labels(t *testing.T) { + m1 := &MockSink{} + m2 := &MockSink{} + fh := &FanoutSink{m1, m2} + + k := []string{"test"} + v := float32(42.0) + l := []Label{{"a", "b"}} + fh.AddSampleWithLabels(k, v, l) + + if !reflect.DeepEqual(m1.keys[0], k) { + t.Fatalf("key not equal") + } + if !reflect.DeepEqual(m2.keys[0], k) { + t.Fatalf("key not equal") + } + if !reflect.DeepEqual(m1.vals[0], v) { + t.Fatalf("val not equal") + } + if !reflect.DeepEqual(m2.vals[0], v) { + t.Fatalf("val not equal") + } + if !reflect.DeepEqual(m1.labels[0], l) { + t.Fatalf("labels not equal") + } + if !reflect.DeepEqual(m2.labels[0], l) { + t.Fatalf("labels not equal") + } +} + func TestNewMetricSinkFromURL(t *testing.T) { for _, tc := range []struct { desc string diff --git a/vendor/github.com/armon/go-metrics/start.go b/vendor/github.com/armon/go-metrics/start.go index 40c8d68c1..46f0c2eb2 100644 --- a/vendor/github.com/armon/go-metrics/start.go +++ b/vendor/github.com/armon/go-metrics/start.go @@ -2,8 +2,11 @@ package metrics import ( "os" + "sync" "sync/atomic" "time" + + "github.com/hashicorp/go-immutable-radix" ) // Config is used to configure metrics settings @@ -11,18 +14,26 @@ type Config struct { ServiceName string // Prefixed with keys to seperate services HostName string // Hostname to use. If not provided and EnableHostname, it will be os.Hostname EnableHostname bool // Enable prefixing gauge values with hostname + EnableHostnameLabel bool // Enable adding hostname to labels + EnableServiceLabel bool // Enable adding service to labels EnableRuntimeMetrics bool // Enables profiling of runtime metrics (GC, Goroutines, Memory) EnableTypePrefix bool // Prefixes key with a type ("counter", "gauge", "timer") TimerGranularity time.Duration // Granularity of timers. ProfileInterval time.Duration // Interval to profile runtime metrics + + AllowedPrefixes []string // A list of metric prefixes to allow, with '.' as the separator + BlockedPrefixes []string // A list of metric prefixes to block, with '.' as the separator + FilterDefault bool // Whether to allow metrics by default } // Metrics represents an instance of a metrics sink that can // be used to emit type Metrics struct { Config - lastNumGC uint32 - sink MetricSink + lastNumGC uint32 + sink MetricSink + filter *iradix.Tree + filterLock sync.RWMutex } // Shared global metrics instance @@ -43,6 +54,7 @@ func DefaultConfig(serviceName string) *Config { EnableTypePrefix: false, // Disable type prefix TimerGranularity: time.Millisecond, // Timers are in milliseconds ProfileInterval: time.Second, // Poll runtime every second + FilterDefault: true, // Don't filter metrics by default } // Try to get the hostname @@ -56,6 +68,7 @@ func New(conf *Config, sink MetricSink) (*Metrics, error) { met := &Metrics{} met.Config = *conf met.sink = sink + met.UpdateFilter(conf.AllowedPrefixes, conf.BlockedPrefixes) // Start the runtime collector if conf.EnableRuntimeMetrics { @@ -79,6 +92,10 @@ func SetGauge(key []string, val float32) { globalMetrics.Load().(*Metrics).SetGauge(key, val) } +func SetGaugeWithLabels(key []string, val float32, labels []Label) { + globalMetrics.Load().(*Metrics).SetGaugeWithLabels(key, val, labels) +} + func EmitKey(key []string, val float32) { globalMetrics.Load().(*Metrics).EmitKey(key, val) } @@ -87,10 +104,26 @@ func IncrCounter(key []string, val float32) { globalMetrics.Load().(*Metrics).IncrCounter(key, val) } +func IncrCounterWithLabels(key []string, val float32, labels []Label) { + globalMetrics.Load().(*Metrics).IncrCounterWithLabels(key, val, labels) +} + func AddSample(key []string, val float32) { globalMetrics.Load().(*Metrics).AddSample(key, val) } +func AddSampleWithLabels(key []string, val float32, labels []Label) { + globalMetrics.Load().(*Metrics).AddSampleWithLabels(key, val, labels) +} + func MeasureSince(key []string, start time.Time) { globalMetrics.Load().(*Metrics).MeasureSince(key, start) } + +func MeasureSinceWithLabels(key []string, start time.Time, labels []Label) { + globalMetrics.Load().(*Metrics).MeasureSinceWithLabels(key, start, labels) +} + +func UpdateFilter(allow, block []string) { + globalMetrics.Load().(*Metrics).UpdateFilter(allow, block) +} diff --git a/vendor/github.com/armon/go-metrics/start_test.go b/vendor/github.com/armon/go-metrics/start_test.go index 96b73d956..f85a5bec3 100644 --- a/vendor/github.com/armon/go-metrics/start_test.go +++ b/vendor/github.com/armon/go-metrics/start_test.go @@ -30,6 +30,7 @@ func TestDefaultConfig(t *testing.T) { t.Fatalf("bad interval") } } + func Test_GlobalMetrics(t *testing.T) { var tests = []struct { desc string @@ -46,7 +47,7 @@ func Test_GlobalMetrics(t *testing.T) { for _, tt := range tests { t.Run(tt.desc, func(t *testing.T) { s := &MockSink{} - globalMetrics.Store(&Metrics{sink: s}) + globalMetrics.Store(&Metrics{Config: Config{FilterDefault: true}, sink: s}) tt.fn(tt.key, tt.val) if got, want := s.keys[0], tt.key; !reflect.DeepEqual(got, want) { t.Fatalf("got key %s want %s", got, want) @@ -58,9 +59,83 @@ func Test_GlobalMetrics(t *testing.T) { } } +func Test_GlobalMetrics_Labels(t *testing.T) { + labels := []Label{{"a", "b"}} + var tests = []struct { + desc string + key []string + val float32 + fn func([]string, float32, []Label) + labels []Label + }{ + {"SetGaugeWithLabels", []string{"test"}, 42, SetGaugeWithLabels, labels}, + {"IncrCounterWithLabels", []string{"test"}, 42, IncrCounterWithLabels, labels}, + {"AddSampleWithLabels", []string{"test"}, 42, AddSampleWithLabels, labels}, + } + + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + s := &MockSink{} + globalMetrics.Store(&Metrics{Config: Config{FilterDefault: true}, sink: s}) + tt.fn(tt.key, tt.val, tt.labels) + if got, want := s.keys[0], tt.key; !reflect.DeepEqual(got, want) { + t.Fatalf("got key %s want %s", got, want) + } + if got, want := s.vals[0], tt.val; !reflect.DeepEqual(got, want) { + t.Fatalf("got val %s want %s", got, want) + } + if got, want := s.labels[0], tt.labels; !reflect.DeepEqual(got, want) { + t.Fatalf("got val %s want %s", got, want) + } + }) + } +} + +func Test_GlobalMetrics_DefaultLabels(t *testing.T) { + config := Config{ + HostName: "host1", + ServiceName: "redis", + EnableHostnameLabel: true, + EnableServiceLabel: true, + FilterDefault: true, + } + labels := []Label{ + {"host", config.HostName}, + {"service", config.ServiceName}, + } + var tests = []struct { + desc string + key []string + val float32 + fn func([]string, float32, []Label) + labels []Label + }{ + {"SetGaugeWithLabels", []string{"test"}, 42, SetGaugeWithLabels, labels}, + {"IncrCounterWithLabels", []string{"test"}, 42, IncrCounterWithLabels, labels}, + {"AddSampleWithLabels", []string{"test"}, 42, AddSampleWithLabels, labels}, + } + + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + s := &MockSink{} + globalMetrics.Store(&Metrics{Config: config, sink: s}) + tt.fn(tt.key, tt.val, nil) + if got, want := s.keys[0], tt.key; !reflect.DeepEqual(got, want) { + t.Fatalf("got key %s want %s", got, want) + } + if got, want := s.vals[0], tt.val; !reflect.DeepEqual(got, want) { + t.Fatalf("got val %s want %s", got, want) + } + if got, want := s.labels[0], tt.labels; !reflect.DeepEqual(got, want) { + t.Fatalf("got val %s want %s", got, want) + } + }) + } +} + func Test_GlobalMetrics_MeasureSince(t *testing.T) { s := &MockSink{} - m := &Metrics{sink: s, Config: Config{TimerGranularity: time.Millisecond}} + m := &Metrics{sink: s, Config: Config{TimerGranularity: time.Millisecond, FilterDefault: true}} globalMetrics.Store(m) k := []string{"test"} @@ -73,6 +148,34 @@ func Test_GlobalMetrics_MeasureSince(t *testing.T) { if s.vals[0] > 0.1 { t.Fatalf("val too large %v", s.vals[0]) } + + labels := []Label{{"a", "b"}} + MeasureSinceWithLabels(k, now, labels) + if got, want := s.keys[1], k; !reflect.DeepEqual(got, want) { + t.Fatalf("got key %s want %s", got, want) + } + if s.vals[1] > 0.1 { + t.Fatalf("val too large %v", s.vals[0]) + } + if got, want := s.labels[1], labels; !reflect.DeepEqual(got, want) { + t.Fatalf("got val %s want %s", got, want) + } +} + +func Test_GlobalMetrics_UpdateFilter(t *testing.T) { + globalMetrics.Store(&Metrics{Config: Config{ + AllowedPrefixes: []string{"a"}, + BlockedPrefixes: []string{"b"}, + }}) + UpdateFilter([]string{"c"}, []string{"d"}) + + m := globalMetrics.Load().(*Metrics) + if m.AllowedPrefixes[0] != "c" { + t.Fatalf("bad: %v", m.AllowedPrefixes) + } + if m.BlockedPrefixes[0] != "d" { + t.Fatalf("bad: %v", m.BlockedPrefixes) + } } // Benchmark_GlobalMetrics_Direct/direct-8 5000000 278 ns/op diff --git a/vendor/github.com/armon/go-metrics/statsd.go b/vendor/github.com/armon/go-metrics/statsd.go index 4241e880c..1bfffce46 100644 --- a/vendor/github.com/armon/go-metrics/statsd.go +++ b/vendor/github.com/armon/go-metrics/statsd.go @@ -50,6 +50,11 @@ func (s *StatsdSink) SetGauge(key []string, val float32) { s.pushMetric(fmt.Sprintf("%s:%f|g\n", flatKey, val)) } +func (s *StatsdSink) SetGaugeWithLabels(key []string, val float32, labels []Label) { + flatKey := s.flattenKeyLabels(key, labels) + s.pushMetric(fmt.Sprintf("%s:%f|g\n", flatKey, val)) +} + func (s *StatsdSink) EmitKey(key []string, val float32) { flatKey := s.flattenKey(key) s.pushMetric(fmt.Sprintf("%s:%f|kv\n", flatKey, val)) @@ -60,11 +65,21 @@ func (s *StatsdSink) IncrCounter(key []string, val float32) { s.pushMetric(fmt.Sprintf("%s:%f|c\n", flatKey, val)) } +func (s *StatsdSink) IncrCounterWithLabels(key []string, val float32, labels []Label) { + flatKey := s.flattenKeyLabels(key, labels) + s.pushMetric(fmt.Sprintf("%s:%f|c\n", flatKey, val)) +} + func (s *StatsdSink) AddSample(key []string, val float32) { flatKey := s.flattenKey(key) s.pushMetric(fmt.Sprintf("%s:%f|ms\n", flatKey, val)) } +func (s *StatsdSink) AddSampleWithLabels(key []string, val float32, labels []Label) { + flatKey := s.flattenKeyLabels(key, labels) + s.pushMetric(fmt.Sprintf("%s:%f|ms\n", flatKey, val)) +} + // Flattens the key for formatting, removes spaces func (s *StatsdSink) flattenKey(parts []string) string { joined := strings.Join(parts, ".") @@ -80,6 +95,14 @@ func (s *StatsdSink) flattenKey(parts []string) string { }, joined) } +// Flattens the key along with labels for formatting, removes spaces +func (s *StatsdSink) flattenKeyLabels(parts []string, labels []Label) string { + for _, label := range labels { + parts = append(parts, label.Value) + } + return s.flattenKey(parts) +} + // Does a non-blocking push to the metrics queue func (s *StatsdSink) pushMetric(m string) { select { diff --git a/vendor/github.com/armon/go-metrics/statsd_test.go b/vendor/github.com/armon/go-metrics/statsd_test.go index 0602b213f..bdf36cc00 100644 --- a/vendor/github.com/armon/go-metrics/statsd_test.go +++ b/vendor/github.com/armon/go-metrics/statsd_test.go @@ -66,7 +66,7 @@ func TestStatsd_Conn(t *testing.T) { if err != nil { t.Fatalf("unexpected err %s", err) } - if line != "key.other:2.000000|kv\n" { + if line != "gauge_labels.val.label:2.000000|g\n" { t.Fatalf("bad line %s", line) } @@ -74,7 +74,7 @@ func TestStatsd_Conn(t *testing.T) { if err != nil { t.Fatalf("unexpected err %s", err) } - if line != "counter.me:3.000000|c\n" { + if line != "key.other:3.000000|kv\n" { t.Fatalf("bad line %s", line) } @@ -82,7 +82,31 @@ func TestStatsd_Conn(t *testing.T) { if err != nil { t.Fatalf("unexpected err %s", err) } - if line != "sample.slow_thingy:4.000000|ms\n" { + if line != "counter.me:4.000000|c\n" { + t.Fatalf("bad line %s", line) + } + + line, err = reader.ReadString('\n') + if err != nil { + t.Fatalf("unexpected err %s", err) + } + if line != "counter_labels.me.label:5.000000|c\n" { + t.Fatalf("bad line %s", line) + } + + line, err = reader.ReadString('\n') + if err != nil { + t.Fatalf("unexpected err %s", err) + } + if line != "sample.slow_thingy:6.000000|ms\n" { + t.Fatalf("bad line %s", line) + } + + line, err = reader.ReadString('\n') + if err != nil { + t.Fatalf("unexpected err %s", err) + } + if line != "sample_labels.slow_thingy.label:7.000000|ms\n" { t.Fatalf("bad line %s", line) } @@ -94,9 +118,12 @@ func TestStatsd_Conn(t *testing.T) { } s.SetGauge([]string{"gauge", "val"}, float32(1)) - s.EmitKey([]string{"key", "other"}, float32(2)) - s.IncrCounter([]string{"counter", "me"}, float32(3)) - s.AddSample([]string{"sample", "slow thingy"}, float32(4)) + s.SetGaugeWithLabels([]string{"gauge_labels", "val"}, float32(2), []Label{{"a", "label"}}) + s.EmitKey([]string{"key", "other"}, float32(3)) + s.IncrCounter([]string{"counter", "me"}, float32(4)) + s.IncrCounterWithLabels([]string{"counter_labels", "me"}, float32(5), []Label{{"a", "label"}}) + s.AddSample([]string{"sample", "slow thingy"}, float32(6)) + s.AddSampleWithLabels([]string{"sample_labels", "slow thingy"}, float32(7), []Label{{"a", "label"}}) select { case <-done: diff --git a/vendor/github.com/armon/go-metrics/statsite.go b/vendor/github.com/armon/go-metrics/statsite.go index 572fe0571..6c0d284d2 100644 --- a/vendor/github.com/armon/go-metrics/statsite.go +++ b/vendor/github.com/armon/go-metrics/statsite.go @@ -50,6 +50,11 @@ func (s *StatsiteSink) SetGauge(key []string, val float32) { s.pushMetric(fmt.Sprintf("%s:%f|g\n", flatKey, val)) } +func (s *StatsiteSink) SetGaugeWithLabels(key []string, val float32, labels []Label) { + flatKey := s.flattenKeyLabels(key, labels) + s.pushMetric(fmt.Sprintf("%s:%f|g\n", flatKey, val)) +} + func (s *StatsiteSink) EmitKey(key []string, val float32) { flatKey := s.flattenKey(key) s.pushMetric(fmt.Sprintf("%s:%f|kv\n", flatKey, val)) @@ -60,11 +65,21 @@ func (s *StatsiteSink) IncrCounter(key []string, val float32) { s.pushMetric(fmt.Sprintf("%s:%f|c\n", flatKey, val)) } +func (s *StatsiteSink) IncrCounterWithLabels(key []string, val float32, labels []Label) { + flatKey := s.flattenKeyLabels(key, labels) + s.pushMetric(fmt.Sprintf("%s:%f|c\n", flatKey, val)) +} + func (s *StatsiteSink) AddSample(key []string, val float32) { flatKey := s.flattenKey(key) s.pushMetric(fmt.Sprintf("%s:%f|ms\n", flatKey, val)) } +func (s *StatsiteSink) AddSampleWithLabels(key []string, val float32, labels []Label) { + flatKey := s.flattenKeyLabels(key, labels) + s.pushMetric(fmt.Sprintf("%s:%f|ms\n", flatKey, val)) +} + // Flattens the key for formatting, removes spaces func (s *StatsiteSink) flattenKey(parts []string) string { joined := strings.Join(parts, ".") @@ -80,6 +95,14 @@ func (s *StatsiteSink) flattenKey(parts []string) string { }, joined) } +// Flattens the key along with labels for formatting, removes spaces +func (s *StatsiteSink) flattenKeyLabels(parts []string, labels []Label) string { + for _, label := range labels { + parts = append(parts, label.Value) + } + return s.flattenKey(parts) +} + // Does a non-blocking push to the metrics queue func (s *StatsiteSink) pushMetric(m string) { select { diff --git a/vendor/github.com/armon/go-metrics/statsite_test.go b/vendor/github.com/armon/go-metrics/statsite_test.go index 704474f43..92687889b 100644 --- a/vendor/github.com/armon/go-metrics/statsite_test.go +++ b/vendor/github.com/armon/go-metrics/statsite_test.go @@ -61,7 +61,7 @@ func TestStatsite_Conn(t *testing.T) { if err != nil { t.Fatalf("unexpected err %s", err) } - if line != "key.other:2.000000|kv\n" { + if line != "gauge_labels.val.label:2.000000|g\n" { t.Fatalf("bad line %s", line) } @@ -69,7 +69,7 @@ func TestStatsite_Conn(t *testing.T) { if err != nil { t.Fatalf("unexpected err %s", err) } - if line != "counter.me:3.000000|c\n" { + if line != "key.other:3.000000|kv\n" { t.Fatalf("bad line %s", line) } @@ -77,7 +77,31 @@ func TestStatsite_Conn(t *testing.T) { if err != nil { t.Fatalf("unexpected err %s", err) } - if line != "sample.slow_thingy:4.000000|ms\n" { + if line != "counter.me:4.000000|c\n" { + t.Fatalf("bad line %s", line) + } + + line, err = reader.ReadString('\n') + if err != nil { + t.Fatalf("unexpected err %s", err) + } + if line != "counter_labels.me.label:5.000000|c\n" { + t.Fatalf("bad line %s", line) + } + + line, err = reader.ReadString('\n') + if err != nil { + t.Fatalf("unexpected err %s", err) + } + if line != "sample.slow_thingy:6.000000|ms\n" { + t.Fatalf("bad line %s", line) + } + + line, err = reader.ReadString('\n') + if err != nil { + t.Fatalf("unexpected err %s", err) + } + if line != "sample_labels.slow_thingy.label:7.000000|ms\n" { t.Fatalf("bad line %s", line) } @@ -90,9 +114,12 @@ func TestStatsite_Conn(t *testing.T) { } s.SetGauge([]string{"gauge", "val"}, float32(1)) - s.EmitKey([]string{"key", "other"}, float32(2)) - s.IncrCounter([]string{"counter", "me"}, float32(3)) - s.AddSample([]string{"sample", "slow thingy"}, float32(4)) + s.SetGaugeWithLabels([]string{"gauge_labels", "val"}, float32(2), []Label{{"a", "label"}}) + s.EmitKey([]string{"key", "other"}, float32(3)) + s.IncrCounter([]string{"counter", "me"}, float32(4)) + s.IncrCounterWithLabels([]string{"counter_labels", "me"}, float32(5), []Label{{"a", "label"}}) + s.AddSample([]string{"sample", "slow thingy"}, float32(6)) + s.AddSampleWithLabels([]string{"sample_labels", "slow thingy"}, float32(7), []Label{{"a", "label"}}) select { case <-done: -- cgit v1.2.3-1-g7c22