diff options
Diffstat (limited to 'vendor/github.com/armon')
-rw-r--r--[-rwxr-xr-x] | vendor/github.com/armon/go-metrics/.gitignore | 0 | ||||
-rw-r--r-- | vendor/github.com/armon/go-metrics/inmem.go | 27 | ||||
-rw-r--r-- | vendor/github.com/armon/go-metrics/inmem_test.go | 71 | ||||
-rw-r--r--[-rwxr-xr-x] | vendor/github.com/armon/go-metrics/metrics.go | 0 | ||||
-rw-r--r--[-rwxr-xr-x] | vendor/github.com/armon/go-metrics/sink.go | 45 | ||||
-rw-r--r--[-rwxr-xr-x] | vendor/github.com/armon/go-metrics/sink_test.go | 48 | ||||
-rw-r--r--[-rwxr-xr-x] | vendor/github.com/armon/go-metrics/start.go | 17 | ||||
-rw-r--r--[-rwxr-xr-x] | vendor/github.com/armon/go-metrics/start_test.go | 125 | ||||
-rw-r--r-- | vendor/github.com/armon/go-metrics/statsd.go | 7 | ||||
-rw-r--r-- | vendor/github.com/armon/go-metrics/statsd_test.go | 43 | ||||
-rw-r--r--[-rwxr-xr-x] | vendor/github.com/armon/go-metrics/statsite.go | 7 | ||||
-rw-r--r--[-rwxr-xr-x] | vendor/github.com/armon/go-metrics/statsite_test.go | 43 |
12 files changed, 353 insertions, 80 deletions
diff --git a/vendor/github.com/armon/go-metrics/.gitignore b/vendor/github.com/armon/go-metrics/.gitignore index 8c03ec112..8c03ec112 100755..100644 --- a/vendor/github.com/armon/go-metrics/.gitignore +++ b/vendor/github.com/armon/go-metrics/.gitignore diff --git a/vendor/github.com/armon/go-metrics/inmem.go b/vendor/github.com/armon/go-metrics/inmem.go index 83fb6bba0..ac46443be 100644 --- a/vendor/github.com/armon/go-metrics/inmem.go +++ b/vendor/github.com/armon/go-metrics/inmem.go @@ -3,6 +3,7 @@ package metrics import ( "fmt" "math" + "net/url" "strings" "sync" "time" @@ -25,7 +26,7 @@ type InmemSink struct { // intervals is a slice of the retained intervals intervals []*IntervalMetrics intervalLock sync.RWMutex - + rateDenom float64 } @@ -68,7 +69,7 @@ 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 // The count of emitted pairs per time unit (usually 1 second) Sum float64 // The sum of values SumSq float64 // The sum of squared values Min float64 // Minimum value @@ -105,7 +106,7 @@ func (a *AggregateSample) Ingest(v float64, rateDenom float64) { if v > a.Max || a.Count == 1 { a.Max = v } - a.Rate = float64(a.Count)/rateDenom + a.Rate = float64(a.Count) / rateDenom a.LastUpdated = time.Now() } @@ -120,6 +121,24 @@ func (a *AggregateSample) String() string { } } +// NewInmemSinkFromURL creates an InmemSink from a URL. It is used +// (and tested) from NewMetricSinkFromURL. +func NewInmemSinkFromURL(u *url.URL) (MetricSink, error) { + params := u.Query() + + interval, err := time.ParseDuration(params.Get("interval")) + if err != nil { + return nil, fmt.Errorf("Bad 'interval' param: %s", err) + } + + retain, err := time.ParseDuration(params.Get("retain")) + if err != nil { + return nil, fmt.Errorf("Bad 'retain' param: %s", err) + } + + return NewInmemSink(interval, retain), nil +} + // NewInmemSink is used to construct a new in-memory sink. // Uses an aggregation interval and maximum retention period. func NewInmemSink(interval, retain time.Duration) *InmemSink { @@ -128,7 +147,7 @@ func NewInmemSink(interval, retain time.Duration) *InmemSink { interval: interval, retain: retain, maxIntervals: int(retain / interval), - rateDenom: float64(interval.Nanoseconds()) / float64(rateTimeUnit.Nanoseconds()), + rateDenom: float64(interval.Nanoseconds()) / float64(rateTimeUnit.Nanoseconds()), } i.intervals = make([]*IntervalMetrics, 0, i.maxIntervals) return i diff --git a/vendor/github.com/armon/go-metrics/inmem_test.go b/vendor/github.com/armon/go-metrics/inmem_test.go index 1c2455114..ed3b521da 100644 --- a/vendor/github.com/armon/go-metrics/inmem_test.go +++ b/vendor/github.com/armon/go-metrics/inmem_test.go @@ -2,6 +2,8 @@ package metrics import ( "math" + "net/url" + "strings" "testing" "time" ) @@ -99,9 +101,78 @@ func TestInmemSink(t *testing.T) { } } +func TestNewInmemSinkFromURL(t *testing.T) { + for _, tc := range []struct { + desc string + input string + expectErr string + expectInterval time.Duration + expectRetain time.Duration + }{ + { + desc: "interval and duration are set via query params", + input: "inmem://?interval=11s&retain=22s", + expectInterval: duration(t, "11s"), + expectRetain: duration(t, "22s"), + }, + { + desc: "interval is required", + input: "inmem://?retain=22s", + expectErr: "Bad 'interval' param", + }, + { + desc: "interval must be a duration", + input: "inmem://?retain=30s&interval=HIYA", + expectErr: "Bad 'interval' param", + }, + { + desc: "retain is required", + input: "inmem://?interval=30s", + expectErr: "Bad 'retain' param", + }, + { + desc: "retain must be a valid duration", + input: "inmem://?interval=30s&retain=HELLO", + expectErr: "Bad 'retain' param", + }, + } { + t.Run(tc.desc, func(t *testing.T) { + u, err := url.Parse(tc.input) + if err != nil { + t.Fatalf("error parsing URL: %s", err) + } + ms, err := NewInmemSinkFromURL(u) + if tc.expectErr != "" { + if !strings.Contains(err.Error(), tc.expectErr) { + t.Fatalf("expected err: %q, to contain: %q", err, tc.expectErr) + } + } else { + if err != nil { + t.Fatalf("unexpected err: %s", err) + } + is := ms.(*InmemSink) + if is.interval != tc.expectInterval { + t.Fatalf("expected interval %s, got: %s", tc.expectInterval, is.interval) + } + if is.retain != tc.expectRetain { + t.Fatalf("expected retain %s, got: %s", tc.expectRetain, is.retain) + } + } + }) + } +} + func min(a, b int) int { if a < b { return a } return b } + +func duration(t *testing.T, s string) time.Duration { + dur, err := time.ParseDuration(s) + if err != nil { + t.Fatalf("error parsing duration: %s", err) + } + return dur +} diff --git a/vendor/github.com/armon/go-metrics/metrics.go b/vendor/github.com/armon/go-metrics/metrics.go index b818e4182..b818e4182 100755..100644 --- a/vendor/github.com/armon/go-metrics/metrics.go +++ b/vendor/github.com/armon/go-metrics/metrics.go diff --git a/vendor/github.com/armon/go-metrics/sink.go b/vendor/github.com/armon/go-metrics/sink.go index 0c240c2c4..9f7e2f6a2 100755..100644 --- a/vendor/github.com/armon/go-metrics/sink.go +++ b/vendor/github.com/armon/go-metrics/sink.go @@ -1,5 +1,10 @@ package metrics +import ( + "fmt" + "net/url" +) + // The MetricSink interface is used to transmit metrics information // to an external system type MetricSink interface { @@ -50,3 +55,43 @@ func (fh FanoutSink) AddSample(key []string, val float32) { s.AddSample(key, val) } } + +// sinkURLFactoryFunc is an generic interface around the *SinkFromURL() function provided +// by each sink type +type sinkURLFactoryFunc func(*url.URL) (MetricSink, error) + +// sinkRegistry supports the generic NewMetricSink function by mapping URL +// schemes to metric sink factory functions +var sinkRegistry = map[string]sinkURLFactoryFunc{ + "statsd": NewStatsdSinkFromURL, + "statsite": NewStatsiteSinkFromURL, + "inmem": NewInmemSinkFromURL, +} + +// NewMetricSinkFromURL allows a generic URL input to configure any of the +// supported sinks. The scheme of the URL identifies the type of the sink, the +// and query parameters are used to set options. +// +// "statsd://" - Initializes a StatsdSink. The host and port are passed through +// as the "addr" of the sink +// +// "statsite://" - Initializes a StatsiteSink. The host and port become the +// "addr" of the sink +// +// "inmem://" - Initializes an InmemSink. The host and port are ignored. The +// "interval" and "duration" query parameters must be specified with valid +// durations, see NewInmemSink for details. +func NewMetricSinkFromURL(urlStr string) (MetricSink, error) { + u, err := url.Parse(urlStr) + if err != nil { + return nil, err + } + + sinkURLFactoryFunc := sinkRegistry[u.Scheme] + if sinkURLFactoryFunc == nil { + return nil, fmt.Errorf( + "cannot create metric sink, unrecognized sink name: %q", u.Scheme) + } + + return sinkURLFactoryFunc(u) +} diff --git a/vendor/github.com/armon/go-metrics/sink_test.go b/vendor/github.com/armon/go-metrics/sink_test.go index 15c5d771a..77c5c3278 100755..100644 --- a/vendor/github.com/armon/go-metrics/sink_test.go +++ b/vendor/github.com/armon/go-metrics/sink_test.go @@ -2,6 +2,7 @@ package metrics import ( "reflect" + "strings" "testing" ) @@ -118,3 +119,50 @@ func TestFanoutSink_Sample(t *testing.T) { t.Fatalf("val not equal") } } + +func TestNewMetricSinkFromURL(t *testing.T) { + for _, tc := range []struct { + desc string + input string + expect reflect.Type + expectErr string + }{ + { + desc: "statsd scheme yields a StatsdSink", + input: "statsd://someserver:123", + expect: reflect.TypeOf(&StatsdSink{}), + }, + { + desc: "statsite scheme yields a StatsiteSink", + input: "statsite://someserver:123", + expect: reflect.TypeOf(&StatsiteSink{}), + }, + { + desc: "inmem scheme yields an InmemSink", + input: "inmem://?interval=30s&retain=30s", + expect: reflect.TypeOf(&InmemSink{}), + }, + { + desc: "unknown scheme yields an error", + input: "notasink://whatever", + expectErr: "unrecognized sink name: \"notasink\"", + }, + } { + t.Run(tc.desc, func(t *testing.T) { + ms, err := NewMetricSinkFromURL(tc.input) + if tc.expectErr != "" { + if !strings.Contains(err.Error(), tc.expectErr) { + t.Fatalf("expected err: %q to contain: %q", err, tc.expectErr) + } + } else { + if err != nil { + t.Fatalf("unexpected err: %s", err) + } + got := reflect.TypeOf(ms) + if got != tc.expect { + t.Fatalf("expected return type to be %v, got: %v", tc.expect, got) + } + } + }) + } +} diff --git a/vendor/github.com/armon/go-metrics/start.go b/vendor/github.com/armon/go-metrics/start.go index 44113f100..40c8d68c1 100755..100644 --- a/vendor/github.com/armon/go-metrics/start.go +++ b/vendor/github.com/armon/go-metrics/start.go @@ -2,6 +2,7 @@ package metrics import ( "os" + "sync/atomic" "time" ) @@ -25,11 +26,11 @@ type Metrics struct { } // Shared global metrics instance -var globalMetrics *Metrics +var globalMetrics atomic.Value // *Metrics func init() { // Initialize to a blackhole sink to avoid errors - globalMetrics = &Metrics{sink: &BlackholeSink{}} + globalMetrics.Store(&Metrics{sink: &BlackholeSink{}}) } // DefaultConfig provides a sane default configuration @@ -68,28 +69,28 @@ func New(conf *Config, sink MetricSink) (*Metrics, error) { func NewGlobal(conf *Config, sink MetricSink) (*Metrics, error) { metrics, err := New(conf, sink) if err == nil { - globalMetrics = metrics + globalMetrics.Store(metrics) } return metrics, err } // Proxy all the methods to the globalMetrics instance func SetGauge(key []string, val float32) { - globalMetrics.SetGauge(key, val) + globalMetrics.Load().(*Metrics).SetGauge(key, val) } func EmitKey(key []string, val float32) { - globalMetrics.EmitKey(key, val) + globalMetrics.Load().(*Metrics).EmitKey(key, val) } func IncrCounter(key []string, val float32) { - globalMetrics.IncrCounter(key, val) + globalMetrics.Load().(*Metrics).IncrCounter(key, val) } func AddSample(key []string, val float32) { - globalMetrics.AddSample(key, val) + globalMetrics.Load().(*Metrics).AddSample(key, val) } func MeasureSince(key []string, start time.Time) { - globalMetrics.MeasureSince(key, start) + globalMetrics.Load().(*Metrics).MeasureSince(key, start) } diff --git a/vendor/github.com/armon/go-metrics/start_test.go b/vendor/github.com/armon/go-metrics/start_test.go index 8b3210c15..96b73d956 100755..100644 --- a/vendor/github.com/armon/go-metrics/start_test.go +++ b/vendor/github.com/armon/go-metrics/start_test.go @@ -1,7 +1,10 @@ package metrics import ( + "io/ioutil" + "log" "reflect" + "sync/atomic" "testing" "time" ) @@ -27,84 +30,70 @@ func TestDefaultConfig(t *testing.T) { t.Fatalf("bad interval") } } - -func Test_GlobalMetrics_SetGauge(t *testing.T) { - m := &MockSink{} - globalMetrics = &Metrics{sink: m} - - k := []string{"test"} - v := float32(42.0) - SetGauge(k, v) - - if !reflect.DeepEqual(m.keys[0], k) { - t.Fatalf("key not equal") - } - if !reflect.DeepEqual(m.vals[0], v) { - t.Fatalf("val not equal") - } -} - -func Test_GlobalMetrics_EmitKey(t *testing.T) { - m := &MockSink{} - globalMetrics = &Metrics{sink: m} - - k := []string{"test"} - v := float32(42.0) - EmitKey(k, v) - - if !reflect.DeepEqual(m.keys[0], k) { - t.Fatalf("key not equal") - } - if !reflect.DeepEqual(m.vals[0], v) { - t.Fatalf("val not equal") - } -} - -func Test_GlobalMetrics_IncrCounter(t *testing.T) { - m := &MockSink{} - globalMetrics = &Metrics{sink: m} - - k := []string{"test"} - v := float32(42.0) - IncrCounter(k, v) - - if !reflect.DeepEqual(m.keys[0], k) { - t.Fatalf("key not equal") - } - if !reflect.DeepEqual(m.vals[0], v) { - t.Fatalf("val not equal") - } -} - -func Test_GlobalMetrics_AddSample(t *testing.T) { - m := &MockSink{} - globalMetrics = &Metrics{sink: m} - - k := []string{"test"} - v := float32(42.0) - AddSample(k, v) - - if !reflect.DeepEqual(m.keys[0], k) { - t.Fatalf("key not equal") - } - if !reflect.DeepEqual(m.vals[0], v) { - t.Fatalf("val not equal") +func Test_GlobalMetrics(t *testing.T) { + var tests = []struct { + desc string + key []string + val float32 + fn func([]string, float32) + }{ + {"SetGauge", []string{"test"}, 42, SetGauge}, + {"EmitKey", []string{"test"}, 42, EmitKey}, + {"IncrCounter", []string{"test"}, 42, IncrCounter}, + {"AddSample", []string{"test"}, 42, AddSample}, + } + + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + s := &MockSink{} + globalMetrics.Store(&Metrics{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) + } + if got, want := s.vals[0], tt.val; !reflect.DeepEqual(got, want) { + t.Fatalf("got val %s want %s", got, want) + } + }) } } func Test_GlobalMetrics_MeasureSince(t *testing.T) { - m := &MockSink{} - globalMetrics = &Metrics{sink: m} - globalMetrics.TimerGranularity = time.Millisecond + s := &MockSink{} + m := &Metrics{sink: s, Config: Config{TimerGranularity: time.Millisecond}} + globalMetrics.Store(m) k := []string{"test"} now := time.Now() MeasureSince(k, now) - if !reflect.DeepEqual(m.keys[0], k) { + if !reflect.DeepEqual(s.keys[0], k) { t.Fatalf("key not equal") } - if m.vals[0] > 0.1 { - t.Fatalf("val too large %v", m.vals[0]) + if s.vals[0] > 0.1 { + t.Fatalf("val too large %v", s.vals[0]) } } + +// Benchmark_GlobalMetrics_Direct/direct-8 5000000 278 ns/op +// Benchmark_GlobalMetrics_Direct/atomic.Value-8 5000000 235 ns/op +func Benchmark_GlobalMetrics_Direct(b *testing.B) { + log.SetOutput(ioutil.Discard) + s := &MockSink{} + m := &Metrics{sink: s} + var v atomic.Value + v.Store(m) + k := []string{"test"} + b.Run("direct", func(b *testing.B) { + for i := 0; i < b.N; i++ { + m.IncrCounter(k, 1) + } + }) + b.Run("atomic.Value", func(b *testing.B) { + for i := 0; i < b.N; i++ { + v.Load().(*Metrics).IncrCounter(k, 1) + } + }) + // do something with m so that the compiler does not optimize this away + b.Logf("%d", m.lastNumGC) +} diff --git a/vendor/github.com/armon/go-metrics/statsd.go b/vendor/github.com/armon/go-metrics/statsd.go index 65a5021a0..4241e880c 100644 --- a/vendor/github.com/armon/go-metrics/statsd.go +++ b/vendor/github.com/armon/go-metrics/statsd.go @@ -5,6 +5,7 @@ import ( "fmt" "log" "net" + "net/url" "strings" "time" ) @@ -23,6 +24,12 @@ type StatsdSink struct { metricQueue chan string } +// NewStatsdSinkFromURL creates an StatsdSink from a URL. It is used +// (and tested) from NewMetricSinkFromURL. +func NewStatsdSinkFromURL(u *url.URL) (MetricSink, error) { + return NewStatsdSink(u.Host) +} + // NewStatsdSink is used to create a new StatsdSink func NewStatsdSink(addr string) (*StatsdSink, error) { s := &StatsdSink{ diff --git a/vendor/github.com/armon/go-metrics/statsd_test.go b/vendor/github.com/armon/go-metrics/statsd_test.go index 622eb5d3a..0602b213f 100644 --- a/vendor/github.com/armon/go-metrics/statsd_test.go +++ b/vendor/github.com/armon/go-metrics/statsd_test.go @@ -4,6 +4,8 @@ import ( "bufio" "bytes" "net" + "net/url" + "strings" "testing" "time" ) @@ -103,3 +105,44 @@ func TestStatsd_Conn(t *testing.T) { t.Fatalf("timeout") } } + +func TestNewStatsdSinkFromURL(t *testing.T) { + for _, tc := range []struct { + desc string + input string + expectErr string + expectAddr string + }{ + { + desc: "address is populated", + input: "statsd://statsd.service.consul", + expectAddr: "statsd.service.consul", + }, + { + desc: "address includes port", + input: "statsd://statsd.service.consul:1234", + expectAddr: "statsd.service.consul:1234", + }, + } { + t.Run(tc.desc, func(t *testing.T) { + u, err := url.Parse(tc.input) + if err != nil { + t.Fatalf("error parsing URL: %s", err) + } + ms, err := NewStatsdSinkFromURL(u) + if tc.expectErr != "" { + if !strings.Contains(err.Error(), tc.expectErr) { + t.Fatalf("expected err: %q, to contain: %q", err, tc.expectErr) + } + } else { + if err != nil { + t.Fatalf("unexpected err: %s", err) + } + is := ms.(*StatsdSink) + if is.addr != tc.expectAddr { + t.Fatalf("expected addr %s, got: %s", tc.expectAddr, is.addr) + } + } + }) + } +} diff --git a/vendor/github.com/armon/go-metrics/statsite.go b/vendor/github.com/armon/go-metrics/statsite.go index 68730139a..572fe0571 100755..100644 --- a/vendor/github.com/armon/go-metrics/statsite.go +++ b/vendor/github.com/armon/go-metrics/statsite.go @@ -5,6 +5,7 @@ import ( "fmt" "log" "net" + "net/url" "strings" "time" ) @@ -16,6 +17,12 @@ const ( flushInterval = 100 * time.Millisecond ) +// NewStatsiteSinkFromURL creates an StatsiteSink from a URL. It is used +// (and tested) from NewMetricSinkFromURL. +func NewStatsiteSinkFromURL(u *url.URL) (MetricSink, error) { + return NewStatsiteSink(u.Host) +} + // StatsiteSink provides a MetricSink that can be used with a // statsite metrics server type StatsiteSink struct { diff --git a/vendor/github.com/armon/go-metrics/statsite_test.go b/vendor/github.com/armon/go-metrics/statsite_test.go index d9c744f41..704474f43 100755..100644 --- a/vendor/github.com/armon/go-metrics/statsite_test.go +++ b/vendor/github.com/armon/go-metrics/statsite_test.go @@ -3,6 +3,8 @@ package metrics import ( "bufio" "net" + "net/url" + "strings" "testing" "time" ) @@ -99,3 +101,44 @@ func TestStatsite_Conn(t *testing.T) { t.Fatalf("timeout") } } + +func TestNewStatsiteSinkFromURL(t *testing.T) { + for _, tc := range []struct { + desc string + input string + expectErr string + expectAddr string + }{ + { + desc: "address is populated", + input: "statsd://statsd.service.consul", + expectAddr: "statsd.service.consul", + }, + { + desc: "address includes port", + input: "statsd://statsd.service.consul:1234", + expectAddr: "statsd.service.consul:1234", + }, + } { + t.Run(tc.desc, func(t *testing.T) { + u, err := url.Parse(tc.input) + if err != nil { + t.Fatalf("error parsing URL: %s", err) + } + ms, err := NewStatsiteSinkFromURL(u) + if tc.expectErr != "" { + if !strings.Contains(err.Error(), tc.expectErr) { + t.Fatalf("expected err: %q, to contain: %q", err, tc.expectErr) + } + } else { + if err != nil { + t.Fatalf("unexpected err: %s", err) + } + is := ms.(*StatsiteSink) + if is.addr != tc.expectAddr { + t.Fatalf("expected addr %s, got: %s", tc.expectAddr, is.addr) + } + } + }) + } +} |