summaryrefslogtreecommitdiffstats
path: root/vendor/github.com/armon/go-metrics
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/armon/go-metrics')
-rw-r--r--[-rwxr-xr-x]vendor/github.com/armon/go-metrics/.gitignore0
-rw-r--r--vendor/github.com/armon/go-metrics/inmem.go27
-rw-r--r--vendor/github.com/armon/go-metrics/inmem_test.go71
-rw-r--r--[-rwxr-xr-x]vendor/github.com/armon/go-metrics/metrics.go0
-rw-r--r--[-rwxr-xr-x]vendor/github.com/armon/go-metrics/sink.go45
-rw-r--r--[-rwxr-xr-x]vendor/github.com/armon/go-metrics/sink_test.go48
-rw-r--r--[-rwxr-xr-x]vendor/github.com/armon/go-metrics/start.go17
-rw-r--r--[-rwxr-xr-x]vendor/github.com/armon/go-metrics/start_test.go125
-rw-r--r--vendor/github.com/armon/go-metrics/statsd.go7
-rw-r--r--vendor/github.com/armon/go-metrics/statsd_test.go43
-rw-r--r--[-rwxr-xr-x]vendor/github.com/armon/go-metrics/statsite.go7
-rw-r--r--[-rwxr-xr-x]vendor/github.com/armon/go-metrics/statsite_test.go43
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)
+ }
+ }
+ })
+ }
+}