bandwidth metering on streams
humanize bandwidth output instrument conn.Conn for bandwidth metrics add poll command for continuous bandwidth reporting move bandwidth tracking onto multiaddr net connections another mild refactor of recording locations address concerns from PR lower mock nodes in race test due to increased goroutines per connection
Jeromy committed
Mar 27, 2015 at 15:42 UTC
2c8cb9fc75c1b1820a70a5cc75232c6b9cfb2d43
73 files changed
+6018
-28
Godeps/Godeps.json
+4
@@ -233,6 +233,10 @@
233
"ImportPath": "golang.org/x/net/context",
234
"Rev": "7dbad50ab5b31073856416cdcfeb2796d682f844"
235
},
236
+ {
237
+ "ImportPath": "github.com/whyrusleeping/go-metrics",
238
+ "Rev": "1cd8009604ec2238b5a71305a0ecd974066e0e16"
239
+ },
240
{
241
"ImportPath": "gopkg.in/fsnotify.v1",
242
"Comment": "v1.2.0",
Godeps/_workspace/src/github.com/whyrusleeping/go-metrics/.gitignore
new
+9
@@ -0,0 +1,9 @@
1
+*.[68]
2
+*.a
3
+*.out
4
+*.swp
5
+_obj
6
+_testmain.go
7
+cmd/metrics-bench/metrics-bench
8
+cmd/metrics-example/metrics-example
9
+cmd/never-read/never-read
Godeps/_workspace/src/github.com/whyrusleeping/go-metrics/LICENSE
new
+29
@@ -0,0 +1,29 @@
1
+Copyright 2012 Richard Crowley. All rights reserved.
2
+
3
+Redistribution and use in source and binary forms, with or without
4
+modification, are permitted provided that the following conditions are
5
+met:
6
+
7
+ 1. Redistributions of source code must retain the above copyright
8
+ notice, this list of conditions and the following disclaimer.
9
+
10
+ 2. Redistributions in binary form must reproduce the above
11
+ copyright notice, this list of conditions and the following
12
+ disclaimer in the documentation and/or other materials provided
13
+ with the distribution.
14
+
15
+THIS SOFTWARE IS PROVIDED BY RICHARD CROWLEY ``AS IS'' AND ANY EXPRESS
16
+OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18
+DISCLAIMED. IN NO EVENT SHALL RICHARD CROWLEY OR CONTRIBUTORS BE LIABLE
19
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
20
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
21
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
22
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
23
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
24
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
25
+THE POSSIBILITY OF SUCH DAMAGE.
26
+
27
+The views and conclusions contained in the software and documentation
28
+are those of the authors and should not be interpreted as representing
29
+official policies, either expressed or implied, of Richard Crowley.
Godeps/_workspace/src/github.com/whyrusleeping/go-metrics/README.md
new
+104
@@ -0,0 +1,104 @@
1
+go-metrics
2
+==========
3
+
4
+Go port of Coda Hale's Metrics library: <https://github.com/codahale/metrics>.
5
+
6
+Documentation: <http://godoc.org/github.com/rcrowley/go-metrics>.
7
+
8
+Usage
9
+-----
10
+
11
+Create and update metrics:
12
+
13
+```go
14
+c := metrics.NewCounter()
15
+metrics.Register("foo", c)
16
+c.Inc(47)
17
+
18
+g := metrics.NewGauge()
19
+metrics.Register("bar", g)
20
+g.Update(47)
21
+
22
+s := metrics.NewExpDecaySample(1028, 0.015) // or metrics.NewUniformSample(1028)
23
+h := metrics.NewHistogram(s)
24
+metrics.Register("baz", h)
25
+h.Update(47)
26
+
27
+m := metrics.NewMeter()
28
+metrics.Register("quux", m)
29
+m.Mark(47)
30
+
31
+t := metrics.NewTimer()
32
+metrics.Register("bang", t)
33
+t.Time(func() {})
34
+t.Update(47)
35
+```
36
+
37
+Periodically log every metric in human-readable form to standard error:
38
+
39
+```go
40
+go metrics.Log(metrics.DefaultRegistry, 60e9, log.New(os.Stderr, "metrics: ", log.Lmicroseconds))
41
+```
42
+
43
+Periodically log every metric in slightly-more-parseable form to syslog:
44
+
45
+```go
46
+w, _ := syslog.Dial("unixgram", "/dev/log", syslog.LOG_INFO, "metrics")
47
+go metrics.Syslog(metrics.DefaultRegistry, 60e9, w)
48
+```
49
+
50
+Periodically emit every metric to Graphite:
51
+
52
+```go
53
+addr, _ := net.ResolveTCPAddr("tcp", "127.0.0.1:2003")
54
+go metrics.Graphite(metrics.DefaultRegistry, 10e9, "metrics", addr)
55
+```
56
+
57
+Periodically emit every metric into InfluxDB:
58
+
59
+```go
60
+import "github.com/rcrowley/go-metrics/influxdb"
61
+
62
+go influxdb.Influxdb(metrics.DefaultRegistry, 10e9, &influxdb.Config{
63
+ Host: "127.0.0.1:8086",
64
+ Database: "metrics",
65
+ Username: "test",
66
+ Password: "test",
67
+})
68
+```
69
+
70
+Periodically upload every metric to Librato:
71
+
72
+```go
73
+import "github.com/rcrowley/go-metrics/librato"
74
+
75
+go librato.Librato(metrics.DefaultRegistry,
76
+ 10e9, // interval
77
+ "example@example.com", // account owner email address
78
+ "token", // Librato API token
79
+ "hostname", // source
80
+ []float64{0.95}, // precentiles to send
81
+ time.Millisecond, // time unit
82
+)
83
+```
84
+
85
+Periodically emit every metric to StatHat:
86
+
87
+```go
88
+import "github.com/rcrowley/go-metrics/stathat"
89
+
90
+go stathat.Stathat(metrics.DefaultRegistry, 10e9, "example@example.com")
91
+```
92
+
93
+Installation
94
+------------
95
+
96
+```sh
97
+go get github.com/rcrowley/go-metrics
98
+```
99
+
100
+StatHat support additionally requires their Go client:
101
+
102
+```sh
103
+go get github.com/stathat/go
104
+```
Godeps/_workspace/src/github.com/whyrusleeping/go-metrics/cmd/metrics-bench/metrics-bench.go
new
+20
@@ -0,0 +1,20 @@
1
+package main
2
+
3
+import (
4
+ "fmt"
5
+ "github.com/rcrowley/go-metrics"
6
+ "time"
7
+)
8
+
9
+func main() {
10
+ r := metrics.NewRegistry()
11
+ for i := 0; i < 10000; i++ {
12
+ r.Register(fmt.Sprintf("counter-%d", i), metrics.NewCounter())
13
+ r.Register(fmt.Sprintf("gauge-%d", i), metrics.NewGauge())
14
+ r.Register(fmt.Sprintf("gaugefloat64-%d", i), metrics.NewGaugeFloat64())
15
+ r.Register(fmt.Sprintf("histogram-uniform-%d", i), metrics.NewHistogram(metrics.NewUniformSample(1028)))
16
+ r.Register(fmt.Sprintf("histogram-exp-%d", i), metrics.NewHistogram(metrics.NewExpDecaySample(1028, 0.015)))
17
+ r.Register(fmt.Sprintf("meter-%d", i), metrics.NewMeter())
18
+ }
19
+ time.Sleep(600e9)
20
+}
Godeps/_workspace/src/github.com/whyrusleeping/go-metrics/cmd/metrics-example/metrics-example.go
new
+154
@@ -0,0 +1,154 @@
1
+package main
2
+
3
+import (
4
+ "errors"
5
+ "github.com/rcrowley/go-metrics"
6
+ // "github.com/rcrowley/go-metrics/stathat"
7
+ "log"
8
+ "math/rand"
9
+ "os"
10
+ // "syslog"
11
+ "time"
12
+)
13
+
14
+const fanout = 10
15
+
16
+func main() {
17
+
18
+ r := metrics.NewRegistry()
19
+
20
+ c := metrics.NewCounter()
21
+ r.Register("foo", c)
22
+ for i := 0; i < fanout; i++ {
23
+ go func() {
24
+ for {
25
+ c.Dec(19)
26
+ time.Sleep(300e6)
27
+ }
28
+ }()
29
+ go func() {
30
+ for {
31
+ c.Inc(47)
32
+ time.Sleep(400e6)
33
+ }
34
+ }()
35
+ }
36
+
37
+ g := metrics.NewGauge()
38
+ r.Register("bar", g)
39
+ for i := 0; i < fanout; i++ {
40
+ go func() {
41
+ for {
42
+ g.Update(19)
43
+ time.Sleep(300e6)
44
+ }
45
+ }()
46
+ go func() {
47
+ for {
48
+ g.Update(47)
49
+ time.Sleep(400e6)
50
+ }
51
+ }()
52
+ }
53
+
54
+ gf := metrics.NewGaugeFloat64()
55
+ r.Register("barfloat64", gf)
56
+ for i := 0; i < fanout; i++ {
57
+ go func() {
58
+ for {
59
+ g.Update(19.0)
60
+ time.Sleep(300e6)
61
+ }
62
+ }()
63
+ go func() {
64
+ for {
65
+ g.Update(47.0)
66
+ time.Sleep(400e6)
67
+ }
68
+ }()
69
+ }
70
+
71
+ hc := metrics.NewHealthcheck(func(h metrics.Healthcheck) {
72
+ if 0 < rand.Intn(2) {
73
+ h.Healthy()
74
+ } else {
75
+ h.Unhealthy(errors.New("baz"))
76
+ }
77
+ })
78
+ r.Register("baz", hc)
79
+
80
+ s := metrics.NewExpDecaySample(1028, 0.015)
81
+ //s := metrics.NewUniformSample(1028)
82
+ h := metrics.NewHistogram(s)
83
+ r.Register("bang", h)
84
+ for i := 0; i < fanout; i++ {
85
+ go func() {
86
+ for {
87
+ h.Update(19)
88
+ time.Sleep(300e6)
89
+ }
90
+ }()
91
+ go func() {
92
+ for {
93
+ h.Update(47)
94
+ time.Sleep(400e6)
95
+ }
96
+ }()
97
+ }
98
+
99
+ m := metrics.NewMeter()
100
+ r.Register("quux", m)
101
+ for i := 0; i < fanout; i++ {
102
+ go func() {
103
+ for {
104
+ m.Mark(19)
105
+ time.Sleep(300e6)
106
+ }
107
+ }()
108
+ go func() {
109
+ for {
110
+ m.Mark(47)
111
+ time.Sleep(400e6)
112
+ }
113
+ }()
114
+ }
115
+
116
+ t := metrics.NewTimer()
117
+ r.Register("hooah", t)
118
+ for i := 0; i < fanout; i++ {
119
+ go func() {
120
+ for {
121
+ t.Time(func() { time.Sleep(300e6) })
122
+ }
123
+ }()
124
+ go func() {
125
+ for {
126
+ t.Time(func() { time.Sleep(400e6) })
127
+ }
128
+ }()
129
+ }
130
+
131
+ metrics.RegisterDebugGCStats(r)
132
+ go metrics.CaptureDebugGCStats(r, 5e9)
133
+
134
+ metrics.RegisterRuntimeMemStats(r)
135
+ go metrics.CaptureRuntimeMemStats(r, 5e9)
136
+
137
+ metrics.Log(r, 60e9, log.New(os.Stderr, "metrics: ", log.Lmicroseconds))
138
+
139
+ /*
140
+ w, err := syslog.Dial("unixgram", "/dev/log", syslog.LOG_INFO, "metrics")
141
+ if nil != err { log.Fatalln(err) }
142
+ metrics.Syslog(r, 60e9, w)
143
+ */
144
+
145
+ /*
146
+ addr, _ := net.ResolveTCPAddr("tcp", "127.0.0.1:2003")
147
+ metrics.Graphite(r, 10e9, "metrics", addr)
148
+ */
149
+
150
+ /*
151
+ stathat.Stathat(r, 10e9, "example@example.com")
152
+ */
153
+
154
+}
Godeps/_workspace/src/github.com/whyrusleeping/go-metrics/cmd/never-read/never-read.go
new
+22
@@ -0,0 +1,22 @@
1
+package main
2
+
3
+import (
4
+ "log"
5
+ "net"
6
+)
7
+
8
+func main() {
9
+ addr, _ := net.ResolveTCPAddr("tcp", "127.0.0.1:2003")
10
+ l, err := net.ListenTCP("tcp", addr)
11
+ if nil != err {
12
+ log.Fatalln(err)
13
+ }
14
+ log.Println("listening", l.Addr())
15
+ for {
16
+ c, err := l.AcceptTCP()
17
+ if nil != err {
18
+ log.Fatalln(err)
19
+ }
20
+ log.Println("accepted", c.RemoteAddr())
21
+ }
22
+}
Godeps/_workspace/src/github.com/whyrusleeping/go-metrics/counter.go
new
+112
@@ -0,0 +1,112 @@
1
+package metrics
2
+
3
+import "sync/atomic"
4
+
5
+// Counters hold an int64 value that can be incremented and decremented.
6
+type Counter interface {
7
+ Clear()
8
+ Count() int64
9
+ Dec(int64)
10
+ Inc(int64)
11
+ Snapshot() Counter
12
+}
13
+
14
+// GetOrRegisterCounter returns an existing Counter or constructs and registers
15
+// a new StandardCounter.
16
+func GetOrRegisterCounter(name string, r Registry) Counter {
17
+ if nil == r {
18
+ r = DefaultRegistry
19
+ }
20
+ return r.GetOrRegister(name, NewCounter).(Counter)
21
+}
22
+
23
+// NewCounter constructs a new StandardCounter.
24
+func NewCounter() Counter {
25
+ if UseNilMetrics {
26
+ return NilCounter{}
27
+ }
28
+ return &StandardCounter{0}
29
+}
30
+
31
+// NewRegisteredCounter constructs and registers a new StandardCounter.
32
+func NewRegisteredCounter(name string, r Registry) Counter {
33
+ c := NewCounter()
34
+ if nil == r {
35
+ r = DefaultRegistry
36
+ }
37
+ r.Register(name, c)
38
+ return c
39
+}
40
+
41
+// CounterSnapshot is a read-only copy of another Counter.
42
+type CounterSnapshot int64
43
+
44
+// Clear panics.
45
+func (CounterSnapshot) Clear() {
46
+ panic("Clear called on a CounterSnapshot")
47
+}
48
+
49
+// Count returns the count at the time the snapshot was taken.
50
+func (c CounterSnapshot) Count() int64 { return int64(c) }
51
+
52
+// Dec panics.
53
+func (CounterSnapshot) Dec(int64) {
54
+ panic("Dec called on a CounterSnapshot")
55
+}
56
+
57
+// Inc panics.
58
+func (CounterSnapshot) Inc(int64) {
59
+ panic("Inc called on a CounterSnapshot")
60
+}
61
+
62
+// Snapshot returns the snapshot.
63
+func (c CounterSnapshot) Snapshot() Counter { return c }
64
+
65
+// NilCounter is a no-op Counter.
66
+type NilCounter struct{}
67
+
68
+// Clear is a no-op.
69
+func (NilCounter) Clear() {}
70
+
71
+// Count is a no-op.
72
+func (NilCounter) Count() int64 { return 0 }
73
+
74
+// Dec is a no-op.
75
+func (NilCounter) Dec(i int64) {}
76
+
77
+// Inc is a no-op.
78
+func (NilCounter) Inc(i int64) {}
79
+
80
+// Snapshot is a no-op.
81
+func (NilCounter) Snapshot() Counter { return NilCounter{} }
82
+
83
+// StandardCounter is the standard implementation of a Counter and uses the
84
+// sync/atomic package to manage a single int64 value.
85
+type StandardCounter struct {
86
+ count int64
87
+}
88
+
89
+// Clear sets the counter to zero.
90
+func (c *StandardCounter) Clear() {
91
+ atomic.StoreInt64(&c.count, 0)
92
+}
93
+
94
+// Count returns the current count.
95
+func (c *StandardCounter) Count() int64 {
96
+ return atomic.LoadInt64(&c.count)
97
+}
98
+
99
+// Dec decrements the counter by the given amount.
100
+func (c *StandardCounter) Dec(i int64) {
101
+ atomic.AddInt64(&c.count, -i)
102
+}
103
+
104
+// Inc increments the counter by the given amount.
105
+func (c *StandardCounter) Inc(i int64) {
106
+ atomic.AddInt64(&c.count, i)
107
+}
108
+
109
+// Snapshot returns a read-only copy of the counter.
110
+func (c *StandardCounter) Snapshot() Counter {
111
+ return CounterSnapshot(c.Count())
112
+}
Godeps/_workspace/src/github.com/whyrusleeping/go-metrics/counter_test.go
new
+77
@@ -0,0 +1,77 @@
1
+package metrics
2
+
3
+import "testing"
4
+
5
+func BenchmarkCounter(b *testing.B) {
6
+ c := NewCounter()
7
+ b.ResetTimer()
8
+ for i := 0; i < b.N; i++ {
9
+ c.Inc(1)
10
+ }
11
+}
12
+
13
+func TestCounterClear(t *testing.T) {
14
+ c := NewCounter()
15
+ c.Inc(1)
16
+ c.Clear()
17
+ if count := c.Count(); 0 != count {
18
+ t.Errorf("c.Count(): 0 != %v\n", count)
19
+ }
20
+}
21
+
22
+func TestCounterDec1(t *testing.T) {
23
+ c := NewCounter()
24
+ c.Dec(1)
25
+ if count := c.Count(); -1 != count {
26
+ t.Errorf("c.Count(): -1 != %v\n", count)
27
+ }
28
+}
29
+
30
+func TestCounterDec2(t *testing.T) {
31
+ c := NewCounter()
32
+ c.Dec(2)
33
+ if count := c.Count(); -2 != count {
34
+ t.Errorf("c.Count(): -2 != %v\n", count)
35
+ }
36
+}
37
+
38
+func TestCounterInc1(t *testing.T) {
39
+ c := NewCounter()
40
+ c.Inc(1)
41
+ if count := c.Count(); 1 != count {
42
+ t.Errorf("c.Count(): 1 != %v\n", count)
43
+ }
44
+}
45
+
46
+func TestCounterInc2(t *testing.T) {
47
+ c := NewCounter()
48
+ c.Inc(2)
49
+ if count := c.Count(); 2 != count {
50
+ t.Errorf("c.Count(): 2 != %v\n", count)
51
+ }
52
+}
53
+
54
+func TestCounterSnapshot(t *testing.T) {
55
+ c := NewCounter()
56
+ c.Inc(1)
57
+ snapshot := c.Snapshot()
58
+ c.Inc(1)
59
+ if count := snapshot.Count(); 1 != count {
60
+ t.Errorf("c.Count(): 1 != %v\n", count)
61
+ }
62
+}
63
+
64
+func TestCounterZero(t *testing.T) {
65
+ c := NewCounter()
66
+ if count := c.Count(); 0 != count {
67
+ t.Errorf("c.Count(): 0 != %v\n", count)
68
+ }
69
+}
70
+
71
+func TestGetOrRegisterCounter(t *testing.T) {
72
+ r := NewRegistry()
73
+ NewRegisteredCounter("foo", r).Inc(47)
74
+ if c := GetOrRegisterCounter("foo", r); 47 != c.Count() {
75
+ t.Fatal(c)
76
+ }
77
+}
Godeps/_workspace/src/github.com/whyrusleeping/go-metrics/debug.go
new
+76
@@ -0,0 +1,76 @@
1
+package metrics
2
+
3
+import (
4
+ "runtime/debug"
5
+ "time"
6
+)
7
+
8
+var (
9
+ debugMetrics struct {
10
+ GCStats struct {
11
+ LastGC Gauge
12
+ NumGC Gauge
13
+ Pause Histogram
14
+ //PauseQuantiles Histogram
15
+ PauseTotal Gauge
16
+ }
17
+ ReadGCStats Timer
18
+ }
19
+ gcStats debug.GCStats
20
+)
21
+
22
+// Capture new values for the Go garbage collector statistics exported in
23
+// debug.GCStats. This is designed to be called as a goroutine.
24
+func CaptureDebugGCStats(r Registry, d time.Duration) {
25
+ for _ = range time.Tick(d) {
26
+ CaptureDebugGCStatsOnce(r)
27
+ }
28
+}
29
+
30
+// Capture new values for the Go garbage collector statistics exported in
31
+// debug.GCStats. This is designed to be called in a background goroutine.
32
+// Giving a registry which has not been given to RegisterDebugGCStats will
33
+// panic.
34
+//
35
+// Be careful (but much less so) with this because debug.ReadGCStats calls
36
+// the C function runtime·lock(runtime·mheap) which, while not a stop-the-world
37
+// operation, isn't something you want to be doing all the time.
38
+func CaptureDebugGCStatsOnce(r Registry) {
39
+ lastGC := gcStats.LastGC
40
+ t := time.Now()
41
+ debug.ReadGCStats(&gcStats)
42
+ debugMetrics.ReadGCStats.UpdateSince(t)
43
+
44
+ debugMetrics.GCStats.LastGC.Update(int64(gcStats.LastGC.UnixNano()))
45
+ debugMetrics.GCStats.NumGC.Update(int64(gcStats.NumGC))
46
+ if lastGC != gcStats.LastGC && 0 < len(gcStats.Pause) {
47
+ debugMetrics.GCStats.Pause.Update(int64(gcStats.Pause[0]))
48
+ }
49
+ //debugMetrics.GCStats.PauseQuantiles.Update(gcStats.PauseQuantiles)
50
+ debugMetrics.GCStats.PauseTotal.Update(int64(gcStats.PauseTotal))
51
+}
52
+
53
+// Register metrics for the Go garbage collector statistics exported in
54
+// debug.GCStats. The metrics are named by their fully-qualified Go symbols,
55
+// i.e. debug.GCStats.PauseTotal.
56
+func RegisterDebugGCStats(r Registry) {
57
+ debugMetrics.GCStats.LastGC = NewGauge()
58
+ debugMetrics.GCStats.NumGC = NewGauge()
59
+ debugMetrics.GCStats.Pause = NewHistogram(NewExpDecaySample(1028, 0.015))
60
+ //debugMetrics.GCStats.PauseQuantiles = NewHistogram(NewExpDecaySample(1028, 0.015))
61
+ debugMetrics.GCStats.PauseTotal = NewGauge()
62
+ debugMetrics.ReadGCStats = NewTimer()
63
+
64
+ r.Register("debug.GCStats.LastGC", debugMetrics.GCStats.LastGC)
65
+ r.Register("debug.GCStats.NumGC", debugMetrics.GCStats.NumGC)
66
+ r.Register("debug.GCStats.Pause", debugMetrics.GCStats.Pause)
67
+ //r.Register("debug.GCStats.PauseQuantiles", debugMetrics.GCStats.PauseQuantiles)
68
+ r.Register("debug.GCStats.PauseTotal", debugMetrics.GCStats.PauseTotal)
69
+ r.Register("debug.ReadGCStats", debugMetrics.ReadGCStats)
70
+}
71
+
72
+// Allocate an initial slice for gcStats.Pause to avoid allocations during
73
+// normal operation.
74
+func init() {
75
+ gcStats.Pause = make([]time.Duration, 11)
76
+}
Godeps/_workspace/src/github.com/whyrusleeping/go-metrics/debug_test.go
new
+48
@@ -0,0 +1,48 @@
1
+package metrics
2
+
3
+import (
4
+ "runtime"
5
+ "runtime/debug"
6
+ "testing"
7
+ "time"
8
+)
9
+
10
+func BenchmarkDebugGCStats(b *testing.B) {
11
+ r := NewRegistry()
12
+ RegisterDebugGCStats(r)
13
+ b.ResetTimer()
14
+ for i := 0; i < b.N; i++ {
15
+ CaptureDebugGCStatsOnce(r)
16
+ }
17
+}
18
+
19
+func TestDebugGCStatsBlocking(t *testing.T) {
20
+ if g := runtime.GOMAXPROCS(0); g < 2 {
21
+ t.Skipf("skipping TestDebugGCMemStatsBlocking with GOMAXPROCS=%d\n", g)
22
+ return
23
+ }
24
+ ch := make(chan int)
25
+ go testDebugGCStatsBlocking(ch)
26
+ var gcStats debug.GCStats
27
+ t0 := time.Now()
28
+ debug.ReadGCStats(&gcStats)
29
+ t1 := time.Now()
30
+ t.Log("i++ during debug.ReadGCStats:", <-ch)
31
+ go testDebugGCStatsBlocking(ch)
32
+ d := t1.Sub(t0)
33
+ t.Log(d)
34
+ time.Sleep(d)
35
+ t.Log("i++ during time.Sleep:", <-ch)
36
+}
37
+
38
+func testDebugGCStatsBlocking(ch chan int) {
39
+ i := 0
40
+ for {
41
+ select {
42
+ case ch <- i:
43
+ return
44
+ default:
45
+ i++
46
+ }
47
+ }
48
+}
Godeps/_workspace/src/github.com/whyrusleeping/go-metrics/ewma.go
new
+123
@@ -0,0 +1,123 @@
1
+package metrics
2
+
3
+import (
4
+ "math"
5
+ "sync"
6
+ "sync/atomic"
7
+)
8
+
9
+// EWMAs continuously calculate an exponentially-weighted moving average
10
+// based on an outside source of clock ticks.
11
+type EWMA interface {
12
+ Rate() float64
13
+ Snapshot() EWMA
14
+ Tick()
15
+ Update(int64)
16
+}
17
+
18
+// NewEWMA constructs a new EWMA with the given alpha.
19
+func NewEWMA(alpha float64) EWMA {
20
+ if UseNilMetrics {
21
+ return NilEWMA{}
22
+ }
23
+ return &StandardEWMA{alpha: alpha}
24
+}
25
+
26
+// NewEWMAFine constructs a new EWMA for a one-second moving average.
27
+func NewEWMAFine() EWMA {
28
+ return NewEWMA(1 - math.Exp(-1.0))
29
+}
30
+
31
+// NewEWMA1 constructs a new EWMA for a one-minute moving average.
32
+func NewEWMA1() EWMA {
33
+ return NewEWMA(1 - math.Exp(-5.0/60.0/1))
34
+}
35
+
36
+// NewEWMA5 constructs a new EWMA for a five-minute moving average.
37
+func NewEWMA5() EWMA {
38
+ return NewEWMA(1 - math.Exp(-5.0/60.0/5))
39
+}
40
+
41
+// NewEWMA15 constructs a new EWMA for a fifteen-minute moving average.
42
+func NewEWMA15() EWMA {
43
+ return NewEWMA(1 - math.Exp(-5.0/60.0/15))
44
+}
45
+
46
+// EWMASnapshot is a read-only copy of another EWMA.
47
+type EWMASnapshot float64
48
+
49
+// Rate returns the rate of events per second at the time the snapshot was
50
+// taken.
51
+func (a EWMASnapshot) Rate() float64 { return float64(a) }
52
+
53
+// Snapshot returns the snapshot.
54
+func (a EWMASnapshot) Snapshot() EWMA { return a }
55
+
56
+// Tick panics.
57
+func (EWMASnapshot) Tick() {
58
+ panic("Tick called on an EWMASnapshot")
59
+}
60
+
61
+// Update panics.
62
+func (EWMASnapshot) Update(int64) {
63
+ panic("Update called on an EWMASnapshot")
64
+}
65
+
66
+// NilEWMA is a no-op EWMA.
67
+type NilEWMA struct{}
68
+
69
+// Rate is a no-op.
70
+func (NilEWMA) Rate() float64 { return 0.0 }
71
+
72
+// Snapshot is a no-op.
73
+func (NilEWMA) Snapshot() EWMA { return NilEWMA{} }
74
+
75
+// Tick is a no-op.
76
+func (NilEWMA) Tick() {}
77
+
78
+// Update is a no-op.
79
+func (NilEWMA) Update(n int64) {}
80
+
81
+// StandardEWMA is the standard implementation of an EWMA and tracks the number
82
+// of uncounted events and processes them on each tick. It uses the
83
+// sync/atomic package to manage uncounted events.
84
+type StandardEWMA struct {
85
+ uncounted int64 // /!\ this should be the first member to ensure 64-bit alignment
86
+ alpha float64
87
+ rate float64
88
+ init bool
89
+ mutex sync.Mutex
90
+}
91
+
92
+// Rate returns the moving average rate of events per second.
93
+func (a *StandardEWMA) Rate() float64 {
94
+ a.mutex.Lock()
95
+ defer a.mutex.Unlock()
96
+ return a.rate * float64(1e9)
97
+}
98
+
99
+// Snapshot returns a read-only copy of the EWMA.
100
+func (a *StandardEWMA) Snapshot() EWMA {
101
+ return EWMASnapshot(a.Rate())
102
+}
103
+
104
+// Tick ticks the clock to update the moving average. It assumes it is called
105
+// every five seconds.
106
+func (a *StandardEWMA) Tick() {
107
+ count := atomic.LoadInt64(&a.uncounted)
108
+ atomic.AddInt64(&a.uncounted, -count)
109
+ instantRate := float64(count) / float64(5e9)
110
+ a.mutex.Lock()
111
+ defer a.mutex.Unlock()
112
+ if a.init {
113
+ a.rate += a.alpha * (instantRate - a.rate)
114
+ } else {
115
+ a.init = true
116
+ a.rate = instantRate
117
+ }
118
+}
119
+
120
+// Update adds n uncounted events.
121
+func (a *StandardEWMA) Update(n int64) {
122
+ atomic.AddInt64(&a.uncounted, n)
123
+}
Godeps/_workspace/src/github.com/whyrusleeping/go-metrics/ewma_test.go
new
+225
@@ -0,0 +1,225 @@
1
+package metrics
2
+
3
+import "testing"
4
+
5
+func BenchmarkEWMA(b *testing.B) {
6
+ a := NewEWMA1()
7
+ b.ResetTimer()
8
+ for i := 0; i < b.N; i++ {
9
+ a.Update(1)
10
+ a.Tick()
11
+ }
12
+}
13
+
14
+func TestEWMA1(t *testing.T) {
15
+ a := NewEWMA1()
16
+ a.Update(3)
17
+ a.Tick()
18
+ if rate := a.Rate(); 0.6 != rate {
19
+ t.Errorf("initial a.Rate(): 0.6 != %v\n", rate)
20
+ }
21
+ elapseMinute(a)
22
+ if rate := a.Rate(); 0.22072766470286553 != rate {
23
+ t.Errorf("1 minute a.Rate(): 0.22072766470286553 != %v\n", rate)
24
+ }
25
+ elapseMinute(a)
26
+ if rate := a.Rate(); 0.08120116994196772 != rate {
27
+ t.Errorf("2 minute a.Rate(): 0.08120116994196772 != %v\n", rate)
28
+ }
29
+ elapseMinute(a)
30
+ if rate := a.Rate(); 0.029872241020718428 != rate {
31
+ t.Errorf("3 minute a.Rate(): 0.029872241020718428 != %v\n", rate)
32
+ }
33
+ elapseMinute(a)
34
+ if rate := a.Rate(); 0.01098938333324054 != rate {
35
+ t.Errorf("4 minute a.Rate(): 0.01098938333324054 != %v\n", rate)
36
+ }
37
+ elapseMinute(a)
38
+ if rate := a.Rate(); 0.004042768199451294 != rate {
39
+ t.Errorf("5 minute a.Rate(): 0.004042768199451294 != %v\n", rate)
40
+ }
41
+ elapseMinute(a)
42
+ if rate := a.Rate(); 0.0014872513059998212 != rate {
43
+ t.Errorf("6 minute a.Rate(): 0.0014872513059998212 != %v\n", rate)
44
+ }
45
+ elapseMinute(a)
46
+ if rate := a.Rate(); 0.0005471291793327122 != rate {
47
+ t.Errorf("7 minute a.Rate(): 0.0005471291793327122 != %v\n", rate)
48
+ }
49
+ elapseMinute(a)
50
+ if rate := a.Rate(); 0.00020127757674150815 != rate {
51
+ t.Errorf("8 minute a.Rate(): 0.00020127757674150815 != %v\n", rate)
52
+ }
53
+ elapseMinute(a)
54
+ if rate := a.Rate(); 7.404588245200814e-05 != rate {
55
+ t.Errorf("9 minute a.Rate(): 7.404588245200814e-05 != %v\n", rate)
56
+ }
57
+ elapseMinute(a)
58
+ if rate := a.Rate(); 2.7239957857491083e-05 != rate {
59
+ t.Errorf("10 minute a.Rate(): 2.7239957857491083e-05 != %v\n", rate)
60
+ }
61
+ elapseMinute(a)
62
+ if rate := a.Rate(); 1.0021020474147462e-05 != rate {
63
+ t.Errorf("11 minute a.Rate(): 1.0021020474147462e-05 != %v\n", rate)
64
+ }
65
+ elapseMinute(a)
66
+ if rate := a.Rate(); 3.6865274119969525e-06 != rate {
67
+ t.Errorf("12 minute a.Rate(): 3.6865274119969525e-06 != %v\n", rate)
68
+ }
69
+ elapseMinute(a)
70
+ if rate := a.Rate(); 1.3561976441886433e-06 != rate {
71
+ t.Errorf("13 minute a.Rate(): 1.3561976441886433e-06 != %v\n", rate)
72
+ }
73
+ elapseMinute(a)
74
+ if rate := a.Rate(); 4.989172314621449e-07 != rate {
75
+ t.Errorf("14 minute a.Rate(): 4.989172314621449e-07 != %v\n", rate)
76
+ }
77
+ elapseMinute(a)
78
+ if rate := a.Rate(); 1.8354139230109722e-07 != rate {
79
+ t.Errorf("15 minute a.Rate(): 1.8354139230109722e-07 != %v\n", rate)
80
+ }
81
+}
82
+
83
+func TestEWMA5(t *testing.T) {
84
+ a := NewEWMA5()
85
+ a.Update(3)
86
+ a.Tick()
87
+ if rate := a.Rate(); 0.6 != rate {
88
+ t.Errorf("initial a.Rate(): 0.6 != %v\n", rate)
89
+ }
90
+ elapseMinute(a)
91
+ if rate := a.Rate(); 0.49123845184678905 != rate {
92
+ t.Errorf("1 minute a.Rate(): 0.49123845184678905 != %v\n", rate)
93
+ }
94
+ elapseMinute(a)
95
+ if rate := a.Rate(); 0.4021920276213837 != rate {
96
+ t.Errorf("2 minute a.Rate(): 0.4021920276213837 != %v\n", rate)
97
+ }
98
+ elapseMinute(a)
99
+ if rate := a.Rate(); 0.32928698165641596 != rate {
100
+ t.Errorf("3 minute a.Rate(): 0.32928698165641596 != %v\n", rate)
101
+ }
102
+ elapseMinute(a)
103
+ if rate := a.Rate(); 0.269597378470333 != rate {
104
+ t.Errorf("4 minute a.Rate(): 0.269597378470333 != %v\n", rate)
105
+ }
106
+ elapseMinute(a)
107
+ if rate := a.Rate(); 0.2207276647028654 != rate {
108
+ t.Errorf("5 minute a.Rate(): 0.2207276647028654 != %v\n", rate)
109
+ }
110
+ elapseMinute(a)
111
+ if rate := a.Rate(); 0.18071652714732128 != rate {
112
+ t.Errorf("6 minute a.Rate(): 0.18071652714732128 != %v\n", rate)
113
+ }
114
+ elapseMinute(a)
115
+ if rate := a.Rate(); 0.14795817836496392 != rate {
116
+ t.Errorf("7 minute a.Rate(): 0.14795817836496392 != %v\n", rate)
117
+ }
118
+ elapseMinute(a)
119
+ if rate := a.Rate(); 0.12113791079679326 != rate {
120
+ t.Errorf("8 minute a.Rate(): 0.12113791079679326 != %v\n", rate)
121
+ }
122
+ elapseMinute(a)
123
+ if rate := a.Rate(); 0.09917933293295193 != rate {
124
+ t.Errorf("9 minute a.Rate(): 0.09917933293295193 != %v\n", rate)
125
+ }
126
+ elapseMinute(a)
127
+ if rate := a.Rate(); 0.08120116994196763 != rate {
128
+ t.Errorf("10 minute a.Rate(): 0.08120116994196763 != %v\n", rate)
129
+ }
130
+ elapseMinute(a)
131
+ if rate := a.Rate(); 0.06648189501740036 != rate {
132
+ t.Errorf("11 minute a.Rate(): 0.06648189501740036 != %v\n", rate)
133
+ }
134
+ elapseMinute(a)
135
+ if rate := a.Rate(); 0.05443077197364752 != rate {
136
+ t.Errorf("12 minute a.Rate(): 0.05443077197364752 != %v\n", rate)
137
+ }
138
+ elapseMinute(a)
139
+ if rate := a.Rate(); 0.04456414692860035 != rate {
140
+ t.Errorf("13 minute a.Rate(): 0.04456414692860035 != %v\n", rate)
141
+ }
142
+ elapseMinute(a)
143
+ if rate := a.Rate(); 0.03648603757513079 != rate {
144
+ t.Errorf("14 minute a.Rate(): 0.03648603757513079 != %v\n", rate)
145
+ }
146
+ elapseMinute(a)
147
+ if rate := a.Rate(); 0.0298722410207183831020718428 != rate {
148
+ t.Errorf("15 minute a.Rate(): 0.0298722410207183831020718428 != %v\n", rate)
149
+ }
150
+}
151
+
152
+func TestEWMA15(t *testing.T) {
153
+ a := NewEWMA15()
154
+ a.Update(3)
155
+ a.Tick()
156
+ if rate := a.Rate(); 0.6 != rate {
157
+ t.Errorf("initial a.Rate(): 0.6 != %v\n", rate)
158
+ }
159
+ elapseMinute(a)
160
+ if rate := a.Rate(); 0.5613041910189706 != rate {
161
+ t.Errorf("1 minute a.Rate(): 0.5613041910189706 != %v\n", rate)
162
+ }
163
+ elapseMinute(a)
164
+ if rate := a.Rate(); 0.5251039914257684 != rate {
165
+ t.Errorf("2 minute a.Rate(): 0.5251039914257684 != %v\n", rate)
166
+ }
167
+ elapseMinute(a)
168
+ if rate := a.Rate(); 0.4912384518467888184678905 != rate {
169
+ t.Errorf("3 minute a.Rate(): 0.4912384518467888184678905 != %v\n", rate)
170
+ }
171
+ elapseMinute(a)
172
+ if rate := a.Rate(); 0.459557003018789 != rate {
173
+ t.Errorf("4 minute a.Rate(): 0.459557003018789 != %v\n", rate)
174
+ }
175
+ elapseMinute(a)
176
+ if rate := a.Rate(); 0.4299187863442732 != rate {
177
+ t.Errorf("5 minute a.Rate(): 0.4299187863442732 != %v\n", rate)
178
+ }
179
+ elapseMinute(a)
180
+ if rate := a.Rate(); 0.4021920276213831 != rate {
181
+ t.Errorf("6 minute a.Rate(): 0.4021920276213831 != %v\n", rate)
182
+ }
183
+ elapseMinute(a)
184
+ if rate := a.Rate(); 0.37625345116383313 != rate {
185
+ t.Errorf("7 minute a.Rate(): 0.37625345116383313 != %v\n", rate)
186
+ }
187
+ elapseMinute(a)
188
+ if rate := a.Rate(); 0.3519877317060185 != rate {
189
+ t.Errorf("8 minute a.Rate(): 0.3519877317060185 != %v\n", rate)
190
+ }
191
+ elapseMinute(a)
192
+ if rate := a.Rate(); 0.3292869816564153165641596 != rate {
193
+ t.Errorf("9 minute a.Rate(): 0.3292869816564153165641596 != %v\n", rate)
194
+ }
195
+ elapseMinute(a)
196
+ if rate := a.Rate(); 0.3080502714195546 != rate {
197
+ t.Errorf("10 minute a.Rate(): 0.3080502714195546 != %v\n", rate)
198
+ }
199
+ elapseMinute(a)
200
+ if rate := a.Rate(); 0.2881831806538789 != rate {
201
+ t.Errorf("11 minute a.Rate(): 0.2881831806538789 != %v\n", rate)
202
+ }
203
+ elapseMinute(a)
204
+ if rate := a.Rate(); 0.26959737847033216 != rate {
205
+ t.Errorf("12 minute a.Rate(): 0.26959737847033216 != %v\n", rate)
206
+ }
207
+ elapseMinute(a)
208
+ if rate := a.Rate(); 0.2522102307052083 != rate {
209
+ t.Errorf("13 minute a.Rate(): 0.2522102307052083 != %v\n", rate)
210
+ }
211
+ elapseMinute(a)
212
+ if rate := a.Rate(); 0.23594443252115815 != rate {
213
+ t.Errorf("14 minute a.Rate(): 0.23594443252115815 != %v\n", rate)
214
+ }
215
+ elapseMinute(a)
216
+ if rate := a.Rate(); 0.2207276647028646247028654470286553 != rate {
217
+ t.Errorf("15 minute a.Rate(): 0.2207276647028646247028654470286553 != %v\n", rate)
218
+ }
219
+}
220
+
221
+func elapseMinute(a EWMA) {
222
+ for i := 0; i < 12; i++ {
223
+ a.Tick()
224
+ }
225
+}
Godeps/_workspace/src/github.com/whyrusleeping/go-metrics/gauge.go
new
+84
@@ -0,0 +1,84 @@
1
+package metrics
2
+
3
+import "sync/atomic"
4
+
5
+// Gauges hold an int64 value that can be set arbitrarily.
6
+type Gauge interface {
7
+ Snapshot() Gauge
8
+ Update(int64)
9
+ Value() int64
10
+}
11
+
12
+// GetOrRegisterGauge returns an existing Gauge or constructs and registers a
13
+// new StandardGauge.
14
+func GetOrRegisterGauge(name string, r Registry) Gauge {
15
+ if nil == r {
16
+ r = DefaultRegistry
17
+ }
18
+ return r.GetOrRegister(name, NewGauge).(Gauge)
19
+}
20
+
21
+// NewGauge constructs a new StandardGauge.
22
+func NewGauge() Gauge {
23
+ if UseNilMetrics {
24
+ return NilGauge{}
25
+ }
26
+ return &StandardGauge{0}
27
+}
28
+
29
+// NewRegisteredGauge constructs and registers a new StandardGauge.
30
+func NewRegisteredGauge(name string, r Registry) Gauge {
31
+ c := NewGauge()
32
+ if nil == r {
33
+ r = DefaultRegistry
34
+ }
35
+ r.Register(name, c)
36
+ return c
37
+}
38
+
39
+// GaugeSnapshot is a read-only copy of another Gauge.
40
+type GaugeSnapshot int64
41
+
42
+// Snapshot returns the snapshot.
43
+func (g GaugeSnapshot) Snapshot() Gauge { return g }
44
+
45
+// Update panics.
46
+func (GaugeSnapshot) Update(int64) {
47
+ panic("Update called on a GaugeSnapshot")
48
+}
49
+
50
+// Value returns the value at the time the snapshot was taken.
51
+func (g GaugeSnapshot) Value() int64 { return int64(g) }
52
+
53
+// NilGauge is a no-op Gauge.
54
+type NilGauge struct{}
55
+
56
+// Snapshot is a no-op.
57
+func (NilGauge) Snapshot() Gauge { return NilGauge{} }
58
+
59
+// Update is a no-op.
60
+func (NilGauge) Update(v int64) {}
61
+
62
+// Value is a no-op.
63
+func (NilGauge) Value() int64 { return 0 }
64
+
65
+// StandardGauge is the standard implementation of a Gauge and uses the
66
+// sync/atomic package to manage a single int64 value.
67
+type StandardGauge struct {
68
+ value int64
69
+}
70
+
71
+// Snapshot returns a read-only copy of the gauge.
72
+func (g *StandardGauge) Snapshot() Gauge {
73
+ return GaugeSnapshot(g.Value())
74
+}
75
+
76
+// Update updates the gauge's value.
77
+func (g *StandardGauge) Update(v int64) {
78
+ atomic.StoreInt64(&g.value, v)
79
+}
80
+
81
+// Value returns the gauge's current value.
82
+func (g *StandardGauge) Value() int64 {
83
+ return atomic.LoadInt64(&g.value)
84
+}
Godeps/_workspace/src/github.com/whyrusleeping/go-metrics/gauge_float64.go
new
+91
@@ -0,0 +1,91 @@
1
+package metrics
2
+
3
+import "sync"
4
+
5
+// GaugeFloat64s hold a float64 value that can be set arbitrarily.
6
+type GaugeFloat64 interface {
7
+ Snapshot() GaugeFloat64
8
+ Update(float64)
9
+ Value() float64
10
+}
11
+
12
+// GetOrRegisterGaugeFloat64 returns an existing GaugeFloat64 or constructs and registers a
13
+// new StandardGaugeFloat64.
14
+func GetOrRegisterGaugeFloat64(name string, r Registry) GaugeFloat64 {
15
+ if nil == r {
16
+ r = DefaultRegistry
17
+ }
18
+ return r.GetOrRegister(name, NewGaugeFloat64()).(GaugeFloat64)
19
+}
20
+
21
+// NewGaugeFloat64 constructs a new StandardGaugeFloat64.
22
+func NewGaugeFloat64() GaugeFloat64 {
23
+ if UseNilMetrics {
24
+ return NilGaugeFloat64{}
25
+ }
26
+ return &StandardGaugeFloat64{
27
+ value: 0.0,
28
+ }
29
+}
30
+
31
+// NewRegisteredGaugeFloat64 constructs and registers a new StandardGaugeFloat64.
32
+func NewRegisteredGaugeFloat64(name string, r Registry) GaugeFloat64 {
33
+ c := NewGaugeFloat64()
34
+ if nil == r {
35
+ r = DefaultRegistry
36
+ }
37
+ r.Register(name, c)
38
+ return c
39
+}
40
+
41
+// GaugeFloat64Snapshot is a read-only copy of another GaugeFloat64.
42
+type GaugeFloat64Snapshot float64
43
+
44
+// Snapshot returns the snapshot.
45
+func (g GaugeFloat64Snapshot) Snapshot() GaugeFloat64 { return g }
46
+
47
+// Update panics.
48
+func (GaugeFloat64Snapshot) Update(float64) {
49
+ panic("Update called on a GaugeFloat64Snapshot")
50
+}
51
+
52
+// Value returns the value at the time the snapshot was taken.
53
+func (g GaugeFloat64Snapshot) Value() float64 { return float64(g) }
54
+
55
+// NilGauge is a no-op Gauge.
56
+type NilGaugeFloat64 struct{}
57
+
58
+// Snapshot is a no-op.
59
+func (NilGaugeFloat64) Snapshot() GaugeFloat64 { return NilGaugeFloat64{} }
60
+
61
+// Update is a no-op.
62
+func (NilGaugeFloat64) Update(v float64) {}
63
+
64
+// Value is a no-op.
65
+func (NilGaugeFloat64) Value() float64 { return 0.0 }
66
+
67
+// StandardGaugeFloat64 is the standard implementation of a GaugeFloat64 and uses
68
+// sync.Mutex to manage a single float64 value.
69
+type StandardGaugeFloat64 struct {
70
+ mutex sync.Mutex
71
+ value float64
72
+}
73
+
74
+// Snapshot returns a read-only copy of the gauge.
75
+func (g *StandardGaugeFloat64) Snapshot() GaugeFloat64 {
76
+ return GaugeFloat64Snapshot(g.Value())
77
+}
78
+
79
+// Update updates the gauge's value.
80
+func (g *StandardGaugeFloat64) Update(v float64) {
81
+ g.mutex.Lock()
82
+ defer g.mutex.Unlock()
83
+ g.value = v
84
+}
85
+
86
+// Value returns the gauge's current value.
87
+func (g *StandardGaugeFloat64) Value() float64 {
88
+ g.mutex.Lock()
89
+ defer g.mutex.Unlock()
90
+ return g.value
91
+}
Godeps/_workspace/src/github.com/whyrusleeping/go-metrics/gauge_float64_test.go
new
+38
@@ -0,0 +1,38 @@
1
+package metrics
2
+
3
+import "testing"
4
+
5
+func BenchmarkGuageFloat64(b *testing.B) {
6
+ g := NewGaugeFloat64()
7
+ b.ResetTimer()
8
+ for i := 0; i < b.N; i++ {
9
+ g.Update(float64(i))
10
+ }
11
+}
12
+
13
+func TestGaugeFloat64(t *testing.T) {
14
+ g := NewGaugeFloat64()
15
+ g.Update(float64(47.0))
16
+ if v := g.Value(); float64(47.0) != v {
17
+ t.Errorf("g.Value(): 47.0 != %v\n", v)
18
+ }
19
+}
20
+
21
+func TestGaugeFloat64Snapshot(t *testing.T) {
22
+ g := NewGaugeFloat64()
23
+ g.Update(float64(47.0))
24
+ snapshot := g.Snapshot()
25
+ g.Update(float64(0))
26
+ if v := snapshot.Value(); float64(47.0) != v {
27
+ t.Errorf("g.Value(): 47.0 != %v\n", v)
28
+ }
29
+}
30
+
31
+func TestGetOrRegisterGaugeFloat64(t *testing.T) {
32
+ r := NewRegistry()
33
+ NewRegisteredGaugeFloat64("foo", r).Update(float64(47.0))
34
+ t.Logf("registry: %v", r)
35
+ if g := GetOrRegisterGaugeFloat64("foo", r); float64(47.0) != g.Value() {
36
+ t.Fatal(g)
37
+ }
38
+}
Godeps/_workspace/src/github.com/whyrusleeping/go-metrics/gauge_test.go
new
+37
@@ -0,0 +1,37 @@
1
+package metrics
2
+
3
+import "testing"
4
+
5
+func BenchmarkGuage(b *testing.B) {
6
+ g := NewGauge()
7
+ b.ResetTimer()
8
+ for i := 0; i < b.N; i++ {
9
+ g.Update(int64(i))
10
+ }
11
+}
12
+
13
+func TestGauge(t *testing.T) {
14
+ g := NewGauge()
15
+ g.Update(int64(47))
16
+ if v := g.Value(); 47 != v {
17
+ t.Errorf("g.Value(): 47 != %v\n", v)
18
+ }
19
+}
20
+
21
+func TestGaugeSnapshot(t *testing.T) {
22
+ g := NewGauge()
23
+ g.Update(int64(47))
24
+ snapshot := g.Snapshot()
25
+ g.Update(int64(0))
26
+ if v := snapshot.Value(); 47 != v {
27
+ t.Errorf("g.Value(): 47 != %v\n", v)
28
+ }
29
+}
30
+
31
+func TestGetOrRegisterGauge(t *testing.T) {
32
+ r := NewRegistry()
33
+ NewRegisteredGauge("foo", r).Update(47)
34
+ if g := GetOrRegisterGauge("foo", r); 47 != g.Value() {
35
+ t.Fatal(g)
36
+ }
37
+}
Godeps/_workspace/src/github.com/whyrusleeping/go-metrics/graphite.go
new
+111
@@ -0,0 +1,111 @@
1
+package metrics
2
+
3
+import (
4
+ "bufio"
5
+ "fmt"
6
+ "log"
7
+ "net"
8
+ "strconv"
9
+ "strings"
10
+ "time"
11
+)
12
+
13
+// GraphiteConfig provides a container with configuration parameters for
14
+// the Graphite exporter
15
+type GraphiteConfig struct {
16
+ Addr *net.TCPAddr // Network address to connect to
17
+ Registry Registry // Registry to be exported
18
+ FlushInterval time.Duration // Flush interval
19
+ DurationUnit time.Duration // Time conversion unit for durations
20
+ Prefix string // Prefix to be prepended to metric names
21
+ Percentiles []float64 // Percentiles to export from timers and histograms
22
+}
23
+
24
+// Graphite is a blocking exporter function which reports metrics in r
25
+// to a graphite server located at addr, flushing them every d duration
26
+// and prepending metric names with prefix.
27
+func Graphite(r Registry, d time.Duration, prefix string, addr *net.TCPAddr) {
28
+ GraphiteWithConfig(GraphiteConfig{
29
+ Addr: addr,
30
+ Registry: r,
31
+ FlushInterval: d,
32
+ DurationUnit: time.Nanosecond,
33
+ Prefix: prefix,
34
+ Percentiles: []float64{0.5, 0.75, 0.95, 0.99, 0.999},
35
+ })
36
+}
37
+
38
+// GraphiteWithConfig is a blocking exporter function just like Graphite,
39
+// but it takes a GraphiteConfig instead.
40
+func GraphiteWithConfig(c GraphiteConfig) {
41
+ for _ = range time.Tick(c.FlushInterval) {
42
+ if err := graphite(&c); nil != err {
43
+ log.Println(err)
44
+ }
45
+ }
46
+}
47
+
48
+// GraphiteOnce performs a single submission to Graphite, returning a
49
+// non-nil error on failed connections. This can be used in a loop
50
+// similar to GraphiteWithConfig for custom error handling.
51
+func GraphiteOnce(c GraphiteConfig) error {
52
+ return graphite(&c)
53
+}
54
+
55
+func graphite(c *GraphiteConfig) error {
56
+ now := time.Now().Unix()
57
+ du := float64(c.DurationUnit)
58
+ conn, err := net.DialTCP("tcp", nil, c.Addr)
59
+ if nil != err {
60
+ return err
61
+ }
62
+ defer conn.Close()
63
+ w := bufio.NewWriter(conn)
64
+ c.Registry.Each(func(name string, i interface{}) {
65
+ switch metric := i.(type) {
66
+ case Counter:
67
+ fmt.Fprintf(w, "%s.%s.count %d %d\n", c.Prefix, name, metric.Count(), now)
68
+ case Gauge:
69
+ fmt.Fprintf(w, "%s.%s.value %d %d\n", c.Prefix, name, metric.Value(), now)
70
+ case GaugeFloat64:
71
+ fmt.Fprintf(w, "%s.%s.value %f %d\n", c.Prefix, name, metric.Value(), now)
72
+ case Histogram:
73
+ h := metric.Snapshot()
74
+ ps := h.Percentiles(c.Percentiles)
75
+ fmt.Fprintf(w, "%s.%s.count %d %d\n", c.Prefix, name, h.Count(), now)
76
+ fmt.Fprintf(w, "%s.%s.min %d %d\n", c.Prefix, name, h.Min(), now)
77
+ fmt.Fprintf(w, "%s.%s.max %d %d\n", c.Prefix, name, h.Max(), now)
78
+ fmt.Fprintf(w, "%s.%s.mean %.2f %d\n", c.Prefix, name, h.Mean(), now)
79
+ fmt.Fprintf(w, "%s.%s.std-dev %.2f %d\n", c.Prefix, name, h.StdDev(), now)
80
+ for psIdx, psKey := range c.Percentiles {
81
+ key := strings.Replace(strconv.FormatFloat(psKey*100.0, 'f', -1, 64), ".", "", 1)
82
+ fmt.Fprintf(w, "%s.%s.%s-percentile %.2f %d\n", c.Prefix, name, key, ps[psIdx], now)
83
+ }
84
+ case Meter:
85
+ m := metric.Snapshot()
86
+ fmt.Fprintf(w, "%s.%s.count %d %d\n", c.Prefix, name, m.Count(), now)
87
+ fmt.Fprintf(w, "%s.%s.one-minute %.2f %d\n", c.Prefix, name, m.Rate1(), now)
88
+ fmt.Fprintf(w, "%s.%s.five-minute %.2f %d\n", c.Prefix, name, m.Rate5(), now)
89
+ fmt.Fprintf(w, "%s.%s.fifteen-minute %.2f %d\n", c.Prefix, name, m.Rate15(), now)
90
+ fmt.Fprintf(w, "%s.%s.mean %.2f %d\n", c.Prefix, name, m.RateMean(), now)
91
+ case Timer:
92
+ t := metric.Snapshot()
93
+ ps := t.Percentiles(c.Percentiles)
94
+ fmt.Fprintf(w, "%s.%s.count %d %d\n", c.Prefix, name, t.Count(), now)
95
+ fmt.Fprintf(w, "%s.%s.min %d %d\n", c.Prefix, name, t.Min()/int64(du), now)
96
+ fmt.Fprintf(w, "%s.%s.max %d %d\n", c.Prefix, name, t.Max()/int64(du), now)
97
+ fmt.Fprintf(w, "%s.%s.mean %.2f %d\n", c.Prefix, name, t.Mean()/du, now)
98
+ fmt.Fprintf(w, "%s.%s.std-dev %.2f %d\n", c.Prefix, name, t.StdDev()/du, now)
99
+ for psIdx, psKey := range c.Percentiles {
100
+ key := strings.Replace(strconv.FormatFloat(psKey*100.0, 'f', -1, 64), ".", "", 1)
101
+ fmt.Fprintf(w, "%s.%s.%s-percentile %.2f %d\n", c.Prefix, name, key, ps[psIdx], now)
102
+ }
103
+ fmt.Fprintf(w, "%s.%s.one-minute %.2f %d\n", c.Prefix, name, t.Rate1(), now)
104
+ fmt.Fprintf(w, "%s.%s.five-minute %.2f %d\n", c.Prefix, name, t.Rate5(), now)
105
+ fmt.Fprintf(w, "%s.%s.fifteen-minute %.2f %d\n", c.Prefix, name, t.Rate15(), now)
106
+ fmt.Fprintf(w, "%s.%s.mean-rate %.2f %d\n", c.Prefix, name, t.RateMean(), now)
107
+ }
108
+ w.Flush()
109
+ })
110
+ return nil
111
+}
Godeps/_workspace/src/github.com/whyrusleeping/go-metrics/graphite_test.go
new
+22
@@ -0,0 +1,22 @@
1
+package metrics
2
+
3
+import (
4
+ "net"
5
+ "time"
6
+)
7
+
8
+func ExampleGraphite() {
9
+ addr, _ := net.ResolveTCPAddr("net", ":2003")
10
+ go Graphite(DefaultRegistry, 1*time.Second, "some.prefix", addr)
11
+}
12
+
13
+func ExampleGraphiteWithConfig() {
14
+ addr, _ := net.ResolveTCPAddr("net", ":2003")
15
+ go GraphiteWithConfig(GraphiteConfig{
16
+ Addr: addr,
17
+ Registry: DefaultRegistry,
18
+ FlushInterval: 1 * time.Second,
19
+ DurationUnit: time.Millisecond,
20
+ Percentiles: []float64{ 0.5, 0.75, 0.99, 0.999 },
21
+ })
22
+}
Godeps/_workspace/src/github.com/whyrusleeping/go-metrics/healthcheck.go
new
+61
@@ -0,0 +1,61 @@
1
+package metrics
2
+
3
+// Healthchecks hold an error value describing an arbitrary up/down status.
4
+type Healthcheck interface {
5
+ Check()
6
+ Error() error
7
+ Healthy()
8
+ Unhealthy(error)
9
+}
10
+
11
+// NewHealthcheck constructs a new Healthcheck which will use the given
12
+// function to update its status.
13
+func NewHealthcheck(f func(Healthcheck)) Healthcheck {
14
+ if UseNilMetrics {
15
+ return NilHealthcheck{}
16
+ }
17
+ return &StandardHealthcheck{nil, f}
18
+}
19
+
20
+// NilHealthcheck is a no-op.
21
+type NilHealthcheck struct{}
22
+
23
+// Check is a no-op.
24
+func (NilHealthcheck) Check() {}
25
+
26
+// Error is a no-op.
27
+func (NilHealthcheck) Error() error { return nil }
28
+
29
+// Healthy is a no-op.
30
+func (NilHealthcheck) Healthy() {}
31
+
32
+// Unhealthy is a no-op.
33
+func (NilHealthcheck) Unhealthy(error) {}
34
+
35
+// StandardHealthcheck is the standard implementation of a Healthcheck and
36
+// stores the status and a function to call to update the status.
37
+type StandardHealthcheck struct {
38
+ err error
39
+ f func(Healthcheck)
40
+}
41
+
42
+// Check runs the healthcheck function to update the healthcheck's status.
43
+func (h *StandardHealthcheck) Check() {
44
+ h.f(h)
45
+}
46
+
47
+// Error returns the healthcheck's status, which will be nil if it is healthy.
48
+func (h *StandardHealthcheck) Error() error {
49
+ return h.err
50
+}
51
+
52
+// Healthy marks the healthcheck as healthy.
53
+func (h *StandardHealthcheck) Healthy() {
54
+ h.err = nil
55
+}
56
+
57
+// Unhealthy marks the healthcheck as unhealthy. The error is stored and
58
+// may be retrieved by the Error method.
59
+func (h *StandardHealthcheck) Unhealthy(err error) {
60
+ h.err = err
61
+}
Godeps/_workspace/src/github.com/whyrusleeping/go-metrics/histogram.go
new
+202
@@ -0,0 +1,202 @@
1
+package metrics
2
+
3
+// Histograms calculate distribution statistics from a series of int64 values.
4
+type Histogram interface {
5
+ Clear()
6
+ Count() int64
7
+ Max() int64
8
+ Mean() float64
9
+ Min() int64
10
+ Percentile(float64) float64
11
+ Percentiles([]float64) []float64
12
+ Sample() Sample
13
+ Snapshot() Histogram
14
+ StdDev() float64
15
+ Sum() int64
16
+ Update(int64)
17
+ Variance() float64
18
+}
19
+
20
+// GetOrRegisterHistogram returns an existing Histogram or constructs and
21
+// registers a new StandardHistogram.
22
+func GetOrRegisterHistogram(name string, r Registry, s Sample) Histogram {
23
+ if nil == r {
24
+ r = DefaultRegistry
25
+ }
26
+ return r.GetOrRegister(name, func() Histogram { return NewHistogram(s) }).(Histogram)
27
+}
28
+
29
+// NewHistogram constructs a new StandardHistogram from a Sample.
30
+func NewHistogram(s Sample) Histogram {
31
+ if UseNilMetrics {
32
+ return NilHistogram{}
33
+ }
34
+ return &StandardHistogram{sample: s}
35
+}
36
+
37
+// NewRegisteredHistogram constructs and registers a new StandardHistogram from
38
+// a Sample.
39
+func NewRegisteredHistogram(name string, r Registry, s Sample) Histogram {
40
+ c := NewHistogram(s)
41
+ if nil == r {
42
+ r = DefaultRegistry
43
+ }
44
+ r.Register(name, c)
45
+ return c
46
+}
47
+
48
+// HistogramSnapshot is a read-only copy of another Histogram.
49
+type HistogramSnapshot struct {
50
+ sample *SampleSnapshot
51
+}
52
+
53
+// Clear panics.
54
+func (*HistogramSnapshot) Clear() {
55
+ panic("Clear called on a HistogramSnapshot")
56
+}
57
+
58
+// Count returns the number of samples recorded at the time the snapshot was
59
+// taken.
60
+func (h *HistogramSnapshot) Count() int64 { return h.sample.Count() }
61
+
62
+// Max returns the maximum value in the sample at the time the snapshot was
63
+// taken.
64
+func (h *HistogramSnapshot) Max() int64 { return h.sample.Max() }
65
+
66
+// Mean returns the mean of the values in the sample at the time the snapshot
67
+// was taken.
68
+func (h *HistogramSnapshot) Mean() float64 { return h.sample.Mean() }
69
+
70
+// Min returns the minimum value in the sample at the time the snapshot was
71
+// taken.
72
+func (h *HistogramSnapshot) Min() int64 { return h.sample.Min() }
73
+
74
+// Percentile returns an arbitrary percentile of values in the sample at the
75
+// time the snapshot was taken.
76
+func (h *HistogramSnapshot) Percentile(p float64) float64 {
77
+ return h.sample.Percentile(p)
78
+}
79
+
80
+// Percentiles returns a slice of arbitrary percentiles of values in the sample
81
+// at the time the snapshot was taken.
82
+func (h *HistogramSnapshot) Percentiles(ps []float64) []float64 {
83
+ return h.sample.Percentiles(ps)
84
+}
85
+
86
+// Sample returns the Sample underlying the histogram.
87
+func (h *HistogramSnapshot) Sample() Sample { return h.sample }
88
+
89
+// Snapshot returns the snapshot.
90
+func (h *HistogramSnapshot) Snapshot() Histogram { return h }
91
+
92
+// StdDev returns the standard deviation of the values in the sample at the
93
+// time the snapshot was taken.
94
+func (h *HistogramSnapshot) StdDev() float64 { return h.sample.StdDev() }
95
+
96
+// Sum returns the sum in the sample at the time the snapshot was taken.
97
+func (h *HistogramSnapshot) Sum() int64 { return h.sample.Sum() }
98
+
99
+// Update panics.
100
+func (*HistogramSnapshot) Update(int64) {
101
+ panic("Update called on a HistogramSnapshot")
102
+}
103
+
104
+// Variance returns the variance of inputs at the time the snapshot was taken.
105
+func (h *HistogramSnapshot) Variance() float64 { return h.sample.Variance() }
106
+
107
+// NilHistogram is a no-op Histogram.
108
+type NilHistogram struct{}
109
+
110
+// Clear is a no-op.
111
+func (NilHistogram) Clear() {}
112
+
113
+// Count is a no-op.
114
+func (NilHistogram) Count() int64 { return 0 }
115
+
116
+// Max is a no-op.
117
+func (NilHistogram) Max() int64 { return 0 }
118
+
119
+// Mean is a no-op.
120
+func (NilHistogram) Mean() float64 { return 0.0 }
121
+
122
+// Min is a no-op.
123
+func (NilHistogram) Min() int64 { return 0 }
124
+
125
+// Percentile is a no-op.
126
+func (NilHistogram) Percentile(p float64) float64 { return 0.0 }
127
+
128
+// Percentiles is a no-op.
129
+func (NilHistogram) Percentiles(ps []float64) []float64 {
130
+ return make([]float64, len(ps))
131
+}
132
+
133
+// Sample is a no-op.
134
+func (NilHistogram) Sample() Sample { return NilSample{} }
135
+
136
+// Snapshot is a no-op.
137
+func (NilHistogram) Snapshot() Histogram { return NilHistogram{} }
138
+
139
+// StdDev is a no-op.
140
+func (NilHistogram) StdDev() float64 { return 0.0 }
141
+
142
+// Sum is a no-op.
143
+func (NilHistogram) Sum() int64 { return 0 }
144
+
145
+// Update is a no-op.
146
+func (NilHistogram) Update(v int64) {}
147
+
148
+// Variance is a no-op.
149
+func (NilHistogram) Variance() float64 { return 0.0 }
150
+
151
+// StandardHistogram is the standard implementation of a Histogram and uses a
152
+// Sample to bound its memory use.
153
+type StandardHistogram struct {
154
+ sample Sample
155
+}
156
+
157
+// Clear clears the histogram and its sample.
158
+func (h *StandardHistogram) Clear() { h.sample.Clear() }
159
+
160
+// Count returns the number of samples recorded since the histogram was last
161
+// cleared.
162
+func (h *StandardHistogram) Count() int64 { return h.sample.Count() }
163
+
164
+// Max returns the maximum value in the sample.
165
+func (h *StandardHistogram) Max() int64 { return h.sample.Max() }
166
+
167
+// Mean returns the mean of the values in the sample.
168
+func (h *StandardHistogram) Mean() float64 { return h.sample.Mean() }
169
+
170
+// Min returns the minimum value in the sample.
171
+func (h *StandardHistogram) Min() int64 { return h.sample.Min() }
172
+
173
+// Percentile returns an arbitrary percentile of the values in the sample.
174
+func (h *StandardHistogram) Percentile(p float64) float64 {
175
+ return h.sample.Percentile(p)
176
+}
177
+
178
+// Percentiles returns a slice of arbitrary percentiles of the values in the
179
+// sample.
180
+func (h *StandardHistogram) Percentiles(ps []float64) []float64 {
181
+ return h.sample.Percentiles(ps)
182
+}
183
+
184
+// Sample returns the Sample underlying the histogram.
185
+func (h *StandardHistogram) Sample() Sample { return h.sample }
186
+
187
+// Snapshot returns a read-only copy of the histogram.
188
+func (h *StandardHistogram) Snapshot() Histogram {
189
+ return &HistogramSnapshot{sample: h.sample.Snapshot().(*SampleSnapshot)}
190
+}
191
+
192
+// StdDev returns the standard deviation of the values in the sample.
193
+func (h *StandardHistogram) StdDev() float64 { return h.sample.StdDev() }
194
+
195
+// Sum returns the sum in the sample.
196
+func (h *StandardHistogram) Sum() int64 { return h.sample.Sum() }
197
+
198
+// Update samples a new value.
199
+func (h *StandardHistogram) Update(v int64) { h.sample.Update(v) }
200
+
201
+// Variance returns the variance of the values in the sample.
202
+func (h *StandardHistogram) Variance() float64 { return h.sample.Variance() }
Godeps/_workspace/src/github.com/whyrusleeping/go-metrics/histogram_test.go
new
+95
@@ -0,0 +1,95 @@
1
+package metrics
2
+
3
+import "testing"
4
+
5
+func BenchmarkHistogram(b *testing.B) {
6
+ h := NewHistogram(NewUniformSample(100))
7
+ b.ResetTimer()
8
+ for i := 0; i < b.N; i++ {
9
+ h.Update(int64(i))
10
+ }
11
+}
12
+
13
+func TestGetOrRegisterHistogram(t *testing.T) {
14
+ r := NewRegistry()
15
+ s := NewUniformSample(100)
16
+ NewRegisteredHistogram("foo", r, s).Update(47)
17
+ if h := GetOrRegisterHistogram("foo", r, s); 1 != h.Count() {
18
+ t.Fatal(h)
19
+ }
20
+}
21
+
22
+func TestHistogram10000(t *testing.T) {
23
+ h := NewHistogram(NewUniformSample(100000))
24
+ for i := 1; i <= 10000; i++ {
25
+ h.Update(int64(i))
26
+ }
27
+ testHistogram10000(t, h)
28
+}
29
+
30
+func TestHistogramEmpty(t *testing.T) {
31
+ h := NewHistogram(NewUniformSample(100))
32
+ if count := h.Count(); 0 != count {
33
+ t.Errorf("h.Count(): 0 != %v\n", count)
34
+ }
35
+ if min := h.Min(); 0 != min {
36
+ t.Errorf("h.Min(): 0 != %v\n", min)
37
+ }
38
+ if max := h.Max(); 0 != max {
39
+ t.Errorf("h.Max(): 0 != %v\n", max)
40
+ }
41
+ if mean := h.Mean(); 0.0 != mean {
42
+ t.Errorf("h.Mean(): 0.0 != %v\n", mean)
43
+ }
44
+ if stdDev := h.StdDev(); 0.0 != stdDev {
45
+ t.Errorf("h.StdDev(): 0.0 != %v\n", stdDev)
46
+ }
47
+ ps := h.Percentiles([]float64{0.5, 0.75, 0.99})
48
+ if 0.0 != ps[0] {
49
+ t.Errorf("median: 0.0 != %v\n", ps[0])
50
+ }
51
+ if 0.0 != ps[1] {
52
+ t.Errorf("75th percentile: 0.0 != %v\n", ps[1])
53
+ }
54
+ if 0.0 != ps[2] {
55
+ t.Errorf("99th percentile: 0.0 != %v\n", ps[2])
56
+ }
57
+}
58
+
59
+func TestHistogramSnapshot(t *testing.T) {
60
+ h := NewHistogram(NewUniformSample(100000))
61
+ for i := 1; i <= 10000; i++ {
62
+ h.Update(int64(i))
63
+ }
64
+ snapshot := h.Snapshot()
65
+ h.Update(0)
66
+ testHistogram10000(t, snapshot)
67
+}
68
+
69
+func testHistogram10000(t *testing.T, h Histogram) {
70
+ if count := h.Count(); 10000 != count {
71
+ t.Errorf("h.Count(): 10000 != %v\n", count)
72
+ }
73
+ if min := h.Min(); 1 != min {
74
+ t.Errorf("h.Min(): 1 != %v\n", min)
75
+ }
76
+ if max := h.Max(); 10000 != max {
77
+ t.Errorf("h.Max(): 10000 != %v\n", max)
78
+ }
79
+ if mean := h.Mean(); 5000.5 != mean {
80
+ t.Errorf("h.Mean(): 5000.5 != %v\n", mean)
81
+ }
82
+ if stdDev := h.StdDev(); 2886.751331514372 != stdDev {
83
+ t.Errorf("h.StdDev(): 2886.751331514372 != %v\n", stdDev)
84
+ }
85
+ ps := h.Percentiles([]float64{0.5, 0.75, 0.99})
86
+ if 5000.5 != ps[0] {
87
+ t.Errorf("median: 5000.5 != %v\n", ps[0])
88
+ }
89
+ if 7500.75 != ps[1] {
90
+ t.Errorf("75th percentile: 7500.75 != %v\n", ps[1])
91
+ }
92
+ if 9900.99 != ps[2] {
93
+ t.Errorf("99th percentile: 9900.99 != %v\n", ps[2])
94
+ }
95
+}
Godeps/_workspace/src/github.com/whyrusleeping/go-metrics/influxdb/influxdb.go
new
+114
@@ -0,0 +1,114 @@
1
+package influxdb
2
+
3
+import (
4
+ "fmt"
5
+ influxClient "github.com/influxdb/influxdb/client"
6
+ "github.com/rcrowley/go-metrics"
7
+ "log"
8
+ "time"
9
+)
10
+
11
+type Config struct {
12
+ Host string
13
+ Database string
14
+ Username string
15
+ Password string
16
+}
17
+
18
+func Influxdb(r metrics.Registry, d time.Duration, config *Config) {
19
+ client, err := influxClient.NewClient(&influxClient.ClientConfig{
20
+ Host: config.Host,
21
+ Database: config.Database,
22
+ Username: config.Username,
23
+ Password: config.Password,
24
+ })
25
+ if err != nil {
26
+ log.Println(err)
27
+ return
28
+ }
29
+
30
+ for _ = range time.Tick(d) {
31
+ if err := send(r, client); err != nil {
32
+ log.Println(err)
33
+ }
34
+ }
35
+}
36
+
37
+func send(r metrics.Registry, client *influxClient.Client) error {
38
+ series := []*influxClient.Series{}
39
+
40
+ r.Each(func(name string, i interface{}) {
41
+ now := getCurrentTime()
42
+ switch metric := i.(type) {
43
+ case metrics.Counter:
44
+ series = append(series, &influxClient.Series{
45
+ Name: fmt.Sprintf("%s.count", name),
46
+ Columns: []string{"time", "count"},
47
+ Points: [][]interface{}{
48
+ {now, metric.Count()},
49
+ },
50
+ })
51
+ case metrics.Gauge:
52
+ series = append(series, &influxClient.Series{
53
+ Name: fmt.Sprintf("%s.value", name),
54
+ Columns: []string{"time", "value"},
55
+ Points: [][]interface{}{
56
+ {now, metric.Value()},
57
+ },
58
+ })
59
+ case metrics.GaugeFloat64:
60
+ series = append(series, &influxClient.Series{
61
+ Name: fmt.Sprintf("%s.value", name),
62
+ Columns: []string{"time", "value"},
63
+ Points: [][]interface{}{
64
+ {now, metric.Value()},
65
+ },
66
+ })
67
+ case metrics.Histogram:
68
+ h := metric.Snapshot()
69
+ ps := h.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999})
70
+ series = append(series, &influxClient.Series{
71
+ Name: fmt.Sprintf("%s.histogram", name),
72
+ Columns: []string{"time", "count", "min", "max", "mean", "std-dev",
73
+ "50-percentile", "75-percentile", "95-percentile",
74
+ "99-percentile", "999-percentile"},
75
+ Points: [][]interface{}{
76
+ {now, h.Count(), h.Min(), h.Max(), h.Mean(), h.StdDev(),
77
+ ps[0], ps[1], ps[2], ps[3], ps[4]},
78
+ },
79
+ })
80
+ case metrics.Meter:
81
+ m := metric.Snapshot()
82
+ series = append(series, &influxClient.Series{
83
+ Name: fmt.Sprintf("%s.meter", name),
84
+ Columns: []string{"count", "one-minute",
85
+ "five-minute", "fifteen-minute", "mean"},
86
+ Points: [][]interface{}{
87
+ {m.Count(), m.Rate1(), m.Rate5(), m.Rate15(), m.RateMean()},
88
+ },
89
+ })
90
+ case metrics.Timer:
91
+ h := metric.Snapshot()
92
+ ps := h.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999})
93
+ series = append(series, &influxClient.Series{
94
+ Name: fmt.Sprintf("%s.timer", name),
95
+ Columns: []string{"count", "min", "max", "mean", "std-dev",
96
+ "50-percentile", "75-percentile", "95-percentile",
97
+ "99-percentile", "999-percentile", "one-minute", "five-minute", "fifteen-minute", "mean-rate"},
98
+ Points: [][]interface{}{
99
+ {h.Count(), h.Min(), h.Max(), h.Mean(), h.StdDev(),
100
+ ps[0], ps[1], ps[2], ps[3], ps[4],
101
+ h.Rate1(), h.Rate5(), h.Rate15(), h.RateMean()},
102
+ },
103
+ })
104
+ }
105
+ })
106
+ if err := client.WriteSeries(series); err != nil {
107
+ log.Println(err)
108
+ }
109
+ return nil
110
+}
111
+
112
+func getCurrentTime() int64 {
113
+ return time.Now().UnixNano() / 1000000
114
+}
Godeps/_workspace/src/github.com/whyrusleeping/go-metrics/json.go
new
+83
@@ -0,0 +1,83 @@
1
+package metrics
2
+
3
+import (
4
+ "encoding/json"
5
+ "io"
6
+ "time"
7
+)
8
+
9
+// MarshalJSON returns a byte slice containing a JSON representation of all
10
+// the metrics in the Registry.
11
+func (r StandardRegistry) MarshalJSON() ([]byte, error) {
12
+ data := make(map[string]map[string]interface{})
13
+ r.Each(func(name string, i interface{}) {
14
+ values := make(map[string]interface{})
15
+ switch metric := i.(type) {
16
+ case Counter:
17
+ values["count"] = metric.Count()
18
+ case Gauge:
19
+ values["value"] = metric.Value()
20
+ case GaugeFloat64:
21
+ values["value"] = metric.Value()
22
+ case Healthcheck:
23
+ values["error"] = nil
24
+ metric.Check()
25
+ if err := metric.Error(); nil != err {
26
+ values["error"] = metric.Error().Error()
27
+ }
28
+ case Histogram:
29
+ h := metric.Snapshot()
30
+ ps := h.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999})
31
+ values["count"] = h.Count()
32
+ values["min"] = h.Min()
33
+ values["max"] = h.Max()
34
+ values["mean"] = h.Mean()
35
+ values["stddev"] = h.StdDev()
36
+ values["median"] = ps[0]
37
+ values["75%"] = ps[1]
38
+ values["95%"] = ps[2]
39
+ values["99%"] = ps[3]
40
+ values["99.9%"] = ps[4]
41
+ case Meter:
42
+ m := metric.Snapshot()
43
+ values["count"] = m.Count()
44
+ values["1m.rate"] = m.Rate1()
45
+ values["5m.rate"] = m.Rate5()
46
+ values["15m.rate"] = m.Rate15()
47
+ values["mean.rate"] = m.RateMean()
48
+ case Timer:
49
+ t := metric.Snapshot()
50
+ ps := t.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999})
51
+ values["count"] = t.Count()
52
+ values["min"] = t.Min()
53
+ values["max"] = t.Max()
54
+ values["mean"] = t.Mean()
55
+ values["stddev"] = t.StdDev()
56
+ values["median"] = ps[0]
57
+ values["75%"] = ps[1]
58
+ values["95%"] = ps[2]
59
+ values["99%"] = ps[3]
60
+ values["99.9%"] = ps[4]
61
+ values["1m.rate"] = t.Rate1()
62
+ values["5m.rate"] = t.Rate5()
63
+ values["15m.rate"] = t.Rate15()
64
+ values["mean.rate"] = t.RateMean()
65
+ }
66
+ data[name] = values
67
+ })
68
+ return json.Marshal(data)
69
+}
70
+
71
+// WriteJSON writes metrics from the given registry periodically to the
72
+// specified io.Writer as JSON.
73
+func WriteJSON(r Registry, d time.Duration, w io.Writer) {
74
+ for _ = range time.Tick(d) {
75
+ WriteJSONOnce(r, w)
76
+ }
77
+}
78
+
79
+// WriteJSONOnce writes metrics from the given registry to the specified
80
+// io.Writer as JSON.
81
+func WriteJSONOnce(r Registry, w io.Writer) {
82
+ json.NewEncoder(w).Encode(r)
83
+}
Godeps/_workspace/src/github.com/whyrusleeping/go-metrics/json_test.go
new
+28
@@ -0,0 +1,28 @@
1
+package metrics
2
+
3
+import (
4
+ "bytes"
5
+ "encoding/json"
6
+ "testing"
7
+)
8
+
9
+func TestRegistryMarshallJSON(t *testing.T) {
10
+ b := &bytes.Buffer{}
11
+ enc := json.NewEncoder(b)
12
+ r := NewRegistry()
13
+ r.Register("counter", NewCounter())
14
+ enc.Encode(r)
15
+ if s := b.String(); "{\"counter\":{\"count\":0}}\n" != s {
16
+ t.Fatalf(s)
17
+ }
18
+}
19
+
20
+func TestRegistryWriteJSONOnce(t *testing.T) {
21
+ r := NewRegistry()
22
+ r.Register("counter", NewCounter())
23
+ b := &bytes.Buffer{}
24
+ WriteJSONOnce(r, b)
25
+ if s := b.String(); s != "{\"counter\":{\"count\":0}}\n" {
26
+ t.Fail()
27
+ }
28
+}
Godeps/_workspace/src/github.com/whyrusleeping/go-metrics/librato/client.go
new
+102
@@ -0,0 +1,102 @@
1
+package librato
2
+
3
+import (
4
+ "bytes"
5
+ "encoding/json"
6
+ "fmt"
7
+ "io/ioutil"
8
+ "net/http"
9
+)
10
+
11
+const Operations = "operations"
12
+const OperationsShort = "ops"
13
+
14
+type LibratoClient struct {
15
+ Email, Token string
16
+}
17
+
18
+// property strings
19
+const (
20
+ // display attributes
21
+ Color = "color"
22
+ DisplayMax = "display_max"
23
+ DisplayMin = "display_min"
24
+ DisplayUnitsLong = "display_units_long"
25
+ DisplayUnitsShort = "display_units_short"
26
+ DisplayStacked = "display_stacked"
27
+ DisplayTransform = "display_transform"
28
+ // special gauge display attributes
29
+ SummarizeFunction = "summarize_function"
30
+ Aggregate = "aggregate"
31
+
32
+ // metric keys
33
+ Name = "name"
34
+ Period = "period"
35
+ Description = "description"
36
+ DisplayName = "display_name"
37
+ Attributes = "attributes"
38
+
39
+ // measurement keys
40
+ MeasureTime = "measure_time"
41
+ Source = "source"
42
+ Value = "value"
43
+
44
+ // special gauge keys
45
+ Count = "count"
46
+ Sum = "sum"
47
+ Max = "max"
48
+ Min = "min"
49
+ SumSquares = "sum_squares"
50
+
51
+ // batch keys
52
+ Counters = "counters"
53
+ Gauges = "gauges"
54
+
55
+ MetricsPostUrl = "https://metrics-api.librato.com/v1/metrics"
56
+)
57
+
58
+type Measurement map[string]interface{}
59
+type Metric map[string]interface{}
60
+
61
+type Batch struct {
62
+ Gauges []Measurement `json:"gauges,omitempty"`
63
+ Counters []Measurement `json:"counters,omitempty"`
64
+ MeasureTime int64 `json:"measure_time"`
65
+ Source string `json:"source"`
66
+}
67
+
68
+func (self *LibratoClient) PostMetrics(batch Batch) (err error) {
69
+ var (
70
+ js []byte
71
+ req *http.Request
72
+ resp *http.Response
73
+ )
74
+
75
+ if len(batch.Counters) == 0 && len(batch.Gauges) == 0 {
76
+ return nil
77
+ }
78
+
79
+ if js, err = json.Marshal(batch); err != nil {
80
+ return
81
+ }
82
+
83
+ if req, err = http.NewRequest("POST", MetricsPostUrl, bytes.NewBuffer(js)); err != nil {
84
+ return
85
+ }
86
+
87
+ req.Header.Set("Content-Type", "application/json")
88
+ req.SetBasicAuth(self.Email, self.Token)
89
+
90
+ if resp, err = http.DefaultClient.Do(req); err != nil {
91
+ return
92
+ }
93
+
94
+ if resp.StatusCode != http.StatusOK {
95
+ var body []byte
96
+ if body, err = ioutil.ReadAll(resp.Body); err != nil {
97
+ body = []byte(fmt.Sprintf("(could not fetch response body for error: %s)", err))
98
+ }
99
+ err = fmt.Errorf("Unable to post to Librato: %d %s %s", resp.StatusCode, resp.Status, string(body))
100
+ }
101
+ return
102
+}
Godeps/_workspace/src/github.com/whyrusleeping/go-metrics/librato/librato.go
new
+230
@@ -0,0 +1,230 @@
1
+package librato
2
+
3
+import (
4
+ "fmt"
5
+ "log"
6
+ "math"
7
+ "regexp"
8
+ "time"
9
+
10
+ "github.com/rcrowley/go-metrics"
11
+)
12
+
13
+// a regexp for extracting the unit from time.Duration.String
14
+var unitRegexp = regexp.MustCompile("[^\\d]+$")
15
+
16
+// a helper that turns a time.Duration into librato display attributes for timer metrics
17
+func translateTimerAttributes(d time.Duration) (attrs map[string]interface{}) {
18
+ attrs = make(map[string]interface{})
19
+ attrs[DisplayTransform] = fmt.Sprintf("x/%d", int64(d))
20
+ attrs[DisplayUnitsShort] = string(unitRegexp.Find([]byte(d.String())))
21
+ return
22
+}
23
+
24
+type Reporter struct {
25
+ Email, Token string
26
+ Source string
27
+ Interval time.Duration
28
+ Registry metrics.Registry
29
+ Percentiles []float64 // percentiles to report on histogram metrics
30
+ TimerAttributes map[string]interface{} // units in which timers will be displayed
31
+ intervalSec int64
32
+}
33
+
34
+func NewReporter(r metrics.Registry, d time.Duration, e string, t string, s string, p []float64, u time.Duration) *Reporter {
35
+ return &Reporter{e, t, s, d, r, p, translateTimerAttributes(u), int64(d / time.Second)}
36
+}
37
+
38
+func Librato(r metrics.Registry, d time.Duration, e string, t string, s string, p []float64, u time.Duration) {
39
+ NewReporter(r, d, e, t, s, p, u).Run()
40
+}
41
+
42
+func (self *Reporter) Run() {
43
+ ticker := time.Tick(self.Interval)
44
+ metricsApi := &LibratoClient{self.Email, self.Token}
45
+ for now := range ticker {
46
+ var metrics Batch
47
+ var err error
48
+ if metrics, err = self.BuildRequest(now, self.Registry); err != nil {
49
+ log.Printf("ERROR constructing librato request body %s", err)
50
+ continue
51
+ }
52
+ if err := metricsApi.PostMetrics(metrics); err != nil {
53
+ log.Printf("ERROR sending metrics to librato %s", err)
54
+ continue
55
+ }
56
+ }
57
+}
58
+
59
+// calculate sum of squares from data provided by metrics.Histogram
60
+// see http://en.wikipedia.org/wiki/Standard_deviation#Rapid_calculation_methods
61
+func sumSquares(s metrics.Sample) float64 {
62
+ count := float64(s.Count())
63
+ sumSquared := math.Pow(count*s.Mean(), 2)
64
+ sumSquares := math.Pow(count*s.StdDev(), 2) + sumSquared/count
65
+ if math.IsNaN(sumSquares) {
66
+ return 0.0
67
+ }
68
+ return sumSquares
69
+}
70
+func sumSquaresTimer(t metrics.Timer) float64 {
71
+ count := float64(t.Count())
72
+ sumSquared := math.Pow(count*t.Mean(), 2)
73
+ sumSquares := math.Pow(count*t.StdDev(), 2) + sumSquared/count
74
+ if math.IsNaN(sumSquares) {
75
+ return 0.0
76
+ }
77
+ return sumSquares
78
+}
79
+
80
+func (self *Reporter) BuildRequest(now time.Time, r metrics.Registry) (snapshot Batch, err error) {
81
+ snapshot = Batch{
82
+ // coerce timestamps to a stepping fn so that they line up in Librato graphs
83
+ MeasureTime: (now.Unix() / self.intervalSec) * self.intervalSec,
84
+ Source: self.Source,
85
+ }
86
+ snapshot.Gauges = make([]Measurement, 0)
87
+ snapshot.Counters = make([]Measurement, 0)
88
+ histogramGaugeCount := 1 + len(self.Percentiles)
89
+ r.Each(func(name string, metric interface{}) {
90
+ measurement := Measurement{}
91
+ measurement[Period] = self.Interval.Seconds()
92
+ switch m := metric.(type) {
93
+ case metrics.Counter:
94
+ if m.Count() > 0 {
95
+ measurement[Name] = fmt.Sprintf("%s.%s", name, "count")
96
+ measurement[Value] = float64(m.Count())
97
+ measurement[Attributes] = map[string]interface{}{
98
+ DisplayUnitsLong: Operations,
99
+ DisplayUnitsShort: OperationsShort,
100
+ DisplayMin: "0",
101
+ }
102
+ snapshot.Counters = append(snapshot.Counters, measurement)
103
+ }
104
+ case metrics.Gauge:
105
+ measurement[Name] = name
106
+ measurement[Value] = float64(m.Value())
107
+ snapshot.Gauges = append(snapshot.Gauges, measurement)
108
+ case metrics.GaugeFloat64:
109
+ measurement[Name] = name
110
+ measurement[Value] = float64(m.Value())
111
+ snapshot.Gauges = append(snapshot.Gauges, measurement)
112
+ case metrics.Histogram:
113
+ if m.Count() > 0 {
114
+ gauges := make([]Measurement, histogramGaugeCount, histogramGaugeCount)
115
+ s := m.Sample()
116
+ measurement[Name] = fmt.Sprintf("%s.%s", name, "hist")
117
+ measurement[Count] = uint64(s.Count())
118
+ measurement[Max] = float64(s.Max())
119
+ measurement[Min] = float64(s.Min())
120
+ measurement[Sum] = float64(s.Sum())
121
+ measurement[SumSquares] = sumSquares(s)
122
+ gauges[0] = measurement
123
+ for i, p := range self.Percentiles {
124
+ gauges[i+1] = Measurement{
125
+ Name: fmt.Sprintf("%s.%.2f", measurement[Name], p),
126
+ Value: s.Percentile(p),
127
+ Period: measurement[Period],
128
+ }
129
+ }
130
+ snapshot.Gauges = append(snapshot.Gauges, gauges...)
131
+ }
132
+ case metrics.Meter:
133
+ measurement[Name] = name
134
+ measurement[Value] = float64(m.Count())
135
+ snapshot.Counters = append(snapshot.Counters, measurement)
136
+ snapshot.Gauges = append(snapshot.Gauges,
137
+ Measurement{
138
+ Name: fmt.Sprintf("%s.%s", name, "1min"),
139
+ Value: m.Rate1(),
140
+ Period: int64(self.Interval.Seconds()),
141
+ Attributes: map[string]interface{}{
142
+ DisplayUnitsLong: Operations,
143
+ DisplayUnitsShort: OperationsShort,
144
+ DisplayMin: "0",
145
+ },
146
+ },
147
+ Measurement{
148
+ Name: fmt.Sprintf("%s.%s", name, "5min"),
149
+ Value: m.Rate5(),
150
+ Period: int64(self.Interval.Seconds()),
151
+ Attributes: map[string]interface{}{
152
+ DisplayUnitsLong: Operations,
153
+ DisplayUnitsShort: OperationsShort,
154
+ DisplayMin: "0",
155
+ },
156
+ },
157
+ Measurement{
158
+ Name: fmt.Sprintf("%s.%s", name, "15min"),
159
+ Value: m.Rate15(),
160
+ Period: int64(self.Interval.Seconds()),
161
+ Attributes: map[string]interface{}{
162
+ DisplayUnitsLong: Operations,
163
+ DisplayUnitsShort: OperationsShort,
164
+ DisplayMin: "0",
165
+ },
166
+ },
167
+ )
168
+ case metrics.Timer:
169
+ measurement[Name] = name
170
+ measurement[Value] = float64(m.Count())
171
+ snapshot.Counters = append(snapshot.Counters, measurement)
172
+ if m.Count() > 0 {
173
+ libratoName := fmt.Sprintf("%s.%s", name, "timer.mean")
174
+ gauges := make([]Measurement, histogramGaugeCount, histogramGaugeCount)
175
+ gauges[0] = Measurement{
176
+ Name: libratoName,
177
+ Count: uint64(m.Count()),
178
+ Sum: m.Mean() * float64(m.Count()),
179
+ Max: float64(m.Max()),
180
+ Min: float64(m.Min()),
181
+ SumSquares: sumSquaresTimer(m),
182
+ Period: int64(self.Interval.Seconds()),
183
+ Attributes: self.TimerAttributes,
184
+ }
185
+ for i, p := range self.Percentiles {
186
+ gauges[i+1] = Measurement{
187
+ Name: fmt.Sprintf("%s.timer.%2.0f", name, p*100),
188
+ Value: m.Percentile(p),
189
+ Period: int64(self.Interval.Seconds()),
190
+ Attributes: self.TimerAttributes,
191
+ }
192
+ }
193
+ snapshot.Gauges = append(snapshot.Gauges, gauges...)
194
+ snapshot.Gauges = append(snapshot.Gauges,
195
+ Measurement{
196
+ Name: fmt.Sprintf("%s.%s", name, "rate.1min"),
197
+ Value: m.Rate1(),
198
+ Period: int64(self.Interval.Seconds()),
199
+ Attributes: map[string]interface{}{
200
+ DisplayUnitsLong: Operations,
201
+ DisplayUnitsShort: OperationsShort,
202
+ DisplayMin: "0",
203
+ },
204
+ },
205
+ Measurement{
206
+ Name: fmt.Sprintf("%s.%s", name, "rate.5min"),
207
+ Value: m.Rate5(),
208
+ Period: int64(self.Interval.Seconds()),
209
+ Attributes: map[string]interface{}{
210
+ DisplayUnitsLong: Operations,
211
+ DisplayUnitsShort: OperationsShort,
212
+ DisplayMin: "0",
213
+ },
214
+ },
215
+ Measurement{
216
+ Name: fmt.Sprintf("%s.%s", name, "rate.15min"),
217
+ Value: m.Rate15(),
218
+ Period: int64(self.Interval.Seconds()),
219
+ Attributes: map[string]interface{}{
220
+ DisplayUnitsLong: Operations,
221
+ DisplayUnitsShort: OperationsShort,
222
+ DisplayMin: "0",
223
+ },
224
+ },
225
+ )
226
+ }
227
+ }
228
+ })
229
+ return
230
+}
Godeps/_workspace/src/github.com/whyrusleeping/go-metrics/log.go
new
+70
@@ -0,0 +1,70 @@
1
+package metrics
2
+
3
+import (
4
+ "log"
5
+ "time"
6
+)
7
+
8
+// Output each metric in the given registry periodically using the given
9
+// logger.
10
+func Log(r Registry, d time.Duration, l *log.Logger) {
11
+ for _ = range time.Tick(d) {
12
+ r.Each(func(name string, i interface{}) {
13
+ switch metric := i.(type) {
14
+ case Counter:
15
+ l.Printf("counter %s\n", name)
16
+ l.Printf(" count: %9d\n", metric.Count())
17
+ case Gauge:
18
+ l.Printf("gauge %s\n", name)
19
+ l.Printf(" value: %9d\n", metric.Value())
20
+ case GaugeFloat64:
21
+ l.Printf("gauge %s\n", name)
22
+ l.Printf(" value: %f\n", metric.Value())
23
+ case Healthcheck:
24
+ metric.Check()
25
+ l.Printf("healthcheck %s\n", name)
26
+ l.Printf(" error: %v\n", metric.Error())
27
+ case Histogram:
28
+ h := metric.Snapshot()
29
+ ps := h.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999})
30
+ l.Printf("histogram %s\n", name)
31
+ l.Printf(" count: %9d\n", h.Count())
32
+ l.Printf(" min: %9d\n", h.Min())
33
+ l.Printf(" max: %9d\n", h.Max())
34
+ l.Printf(" mean: %12.2f\n", h.Mean())
35
+ l.Printf(" stddev: %12.2f\n", h.StdDev())
36
+ l.Printf(" median: %12.2f\n", ps[0])
37
+ l.Printf(" 75%%: %12.2f\n", ps[1])
38
+ l.Printf(" 95%%: %12.2f\n", ps[2])
39
+ l.Printf(" 99%%: %12.2f\n", ps[3])
40
+ l.Printf(" 99.9%%: %12.2f\n", ps[4])
41
+ case Meter:
42
+ m := metric.Snapshot()
43
+ l.Printf("meter %s\n", name)
44
+ l.Printf(" count: %9d\n", m.Count())
45
+ l.Printf(" 1-min rate: %12.2f\n", m.Rate1())
46
+ l.Printf(" 5-min rate: %12.2f\n", m.Rate5())
47
+ l.Printf(" 15-min rate: %12.2f\n", m.Rate15())
48
+ l.Printf(" mean rate: %12.2f\n", m.RateMean())
49
+ case Timer:
50
+ t := metric.Snapshot()
51
+ ps := t.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999})
52
+ l.Printf("timer %s\n", name)
53
+ l.Printf(" count: %9d\n", t.Count())
54
+ l.Printf(" min: %9d\n", t.Min())
55
+ l.Printf(" max: %9d\n", t.Max())
56
+ l.Printf(" mean: %12.2f\n", t.Mean())
57
+ l.Printf(" stddev: %12.2f\n", t.StdDev())
58
+ l.Printf(" median: %12.2f\n", ps[0])
59
+ l.Printf(" 75%%: %12.2f\n", ps[1])
60
+ l.Printf(" 95%%: %12.2f\n", ps[2])
61
+ l.Printf(" 99%%: %12.2f\n", ps[3])
62
+ l.Printf(" 99.9%%: %12.2f\n", ps[4])
63
+ l.Printf(" 1-min rate: %12.2f\n", t.Rate1())
64
+ l.Printf(" 5-min rate: %12.2f\n", t.Rate5())
65
+ l.Printf(" 15-min rate: %12.2f\n", t.Rate15())
66
+ l.Printf(" mean rate: %12.2f\n", t.RateMean())
67
+ }
68
+ })
69
+ }
70
+}
Godeps/_workspace/src/github.com/whyrusleeping/go-metrics/memory.md
new
+285
@@ -0,0 +1,285 @@
1
+Memory usage
2
+============
3
+
4
+(Highly unscientific.)
5
+
6
+Command used to gather static memory usage:
7
+
8
+```sh
9
+grep ^Vm "/proc/$(ps fax | grep [m]etrics-bench | awk '{print $1}')/status"
10
+```
11
+
12
+Program used to gather baseline memory usage:
13
+
14
+```go
15
+package main
16
+
17
+import "time"
18
+
19
+func main() {
20
+ time.Sleep(600e9)
21
+}
22
+```
23
+
24
+Baseline
25
+--------
26
+
27
+```
28
+VmPeak: 42604 kB
29
+VmSize: 42604 kB
30
+VmLck: 0 kB
31
+VmHWM: 1120 kB
32
+VmRSS: 1120 kB
33
+VmData: 35460 kB
34
+VmStk: 136 kB
35
+VmExe: 1020 kB
36
+VmLib: 1848 kB
37
+VmPTE: 36 kB
38
+VmSwap: 0 kB
39
+```
40
+
41
+Program used to gather metric memory usage (with other metrics being similar):
42
+
43
+```go
44
+package main
45
+
46
+import (
47
+ "fmt"
48
+ "metrics"
49
+ "time"
50
+)
51
+
52
+func main() {
53
+ fmt.Sprintf("foo")
54
+ metrics.NewRegistry()
55
+ time.Sleep(600e9)
56
+}
57
+```
58
+
59
+1000 counters registered
60
+------------------------
61
+
62
+```
63
+VmPeak: 44016 kB
64
+VmSize: 44016 kB
65
+VmLck: 0 kB
66
+VmHWM: 1928 kB
67
+VmRSS: 1928 kB
68
+VmData: 36868 kB
69
+VmStk: 136 kB
70
+VmExe: 1024 kB
71
+VmLib: 1848 kB
72
+VmPTE: 40 kB
73
+VmSwap: 0 kB
74
+```
75
+
76
+**1.412 kB virtual, TODO 0.808 kB resident per counter.**
77
+
78
+100000 counters registered
79
+--------------------------
80
+
81
+```
82
+VmPeak: 55024 kB
83
+VmSize: 55024 kB
84
+VmLck: 0 kB
85
+VmHWM: 12440 kB
86
+VmRSS: 12440 kB
87
+VmData: 47876 kB
88
+VmStk: 136 kB
89
+VmExe: 1024 kB
90
+VmLib: 1848 kB
91
+VmPTE: 64 kB
92
+VmSwap: 0 kB
93
+```
94
+
95
+**0.1242 kB virtual, 0.1132 kB resident per counter.**
96
+
97
+1000 gauges registered
98
+----------------------
99
+
100
+```
101
+VmPeak: 44012 kB
102
+VmSize: 44012 kB
103
+VmLck: 0 kB
104
+VmHWM: 1928 kB
105
+VmRSS: 1928 kB
106
+VmData: 36868 kB
107
+VmStk: 136 kB
108
+VmExe: 1020 kB
109
+VmLib: 1848 kB
110
+VmPTE: 40 kB
111
+VmSwap: 0 kB
112
+```
113
+
114
+**1.408 kB virtual, 0.808 kB resident per counter.**
115
+
116
+100000 gauges registered
117
+------------------------
118
+
119
+```
120
+VmPeak: 55020 kB
121
+VmSize: 55020 kB
122
+VmLck: 0 kB
123
+VmHWM: 12432 kB
124
+VmRSS: 12432 kB
125
+VmData: 47876 kB
126
+VmStk: 136 kB
127
+VmExe: 1020 kB
128
+VmLib: 1848 kB
129
+VmPTE: 60 kB
130
+VmSwap: 0 kB
131
+```
132
+
133
+**0.12416 kB virtual, 0.11312 resident per gauge.**
134
+
135
+1000 histograms with a uniform sample size of 1028
136
+--------------------------------------------------
137
+
138
+```
139
+VmPeak: 72272 kB
140
+VmSize: 72272 kB
141
+VmLck: 0 kB
142
+VmHWM: 16204 kB
143
+VmRSS: 16204 kB
144
+VmData: 65100 kB
145
+VmStk: 136 kB
146
+VmExe: 1048 kB
147
+VmLib: 1848 kB
148
+VmPTE: 80 kB
149
+VmSwap: 0 kB
150
+```
151
+
152
+**29.668 kB virtual, TODO 15.084 resident per histogram.**
153
+
154
+10000 histograms with a uniform sample size of 1028
155
+---------------------------------------------------
156
+
157
+```
158
+VmPeak: 256912 kB
159
+VmSize: 256912 kB
160
+VmLck: 0 kB
161
+VmHWM: 146204 kB
162
+VmRSS: 146204 kB
163
+VmData: 249740 kB
164
+VmStk: 136 kB
165
+VmExe: 1048 kB
166
+VmLib: 1848 kB
167
+VmPTE: 448 kB
168
+VmSwap: 0 kB
169
+```
170
+
171
+**21.4308 kB virtual, 14.5084 kB resident per histogram.**
172
+
173
+50000 histograms with a uniform sample size of 1028
174
+---------------------------------------------------
175
+
176
+```
177
+VmPeak: 908112 kB
178
+VmSize: 908112 kB
179
+VmLck: 0 kB
180
+VmHWM: 645832 kB
181
+VmRSS: 645588 kB
182
+VmData: 900940 kB
183
+VmStk: 136 kB
184
+VmExe: 1048 kB
185
+VmLib: 1848 kB
186
+VmPTE: 1716 kB
187
+VmSwap: 1544 kB
188
+```
189
+
190
+**17.31016 kB virtual, 12.88936 kB resident per histogram.**
191
+
192
+1000 histograms with an exponentially-decaying sample size of 1028 and alpha of 0.015
193
+-------------------------------------------------------------------------------------
194
+
195
+```
196
+VmPeak: 62480 kB
197
+VmSize: 62480 kB
198
+VmLck: 0 kB
199
+VmHWM: 11572 kB
200
+VmRSS: 11572 kB
201
+VmData: 55308 kB
202
+VmStk: 136 kB
203
+VmExe: 1048 kB
204
+VmLib: 1848 kB
205
+VmPTE: 64 kB
206
+VmSwap: 0 kB
207
+```
208
+
209
+**19.876 kB virtual, 10.452 kB resident per histogram.**
210
+
211
+10000 histograms with an exponentially-decaying sample size of 1028 and alpha of 0.015
212
+--------------------------------------------------------------------------------------
213
+
214
+```
215
+VmPeak: 153296 kB
216
+VmSize: 153296 kB
217
+VmLck: 0 kB
218
+VmHWM: 101176 kB
219
+VmRSS: 101176 kB
220
+VmData: 146124 kB
221
+VmStk: 136 kB
222
+VmExe: 1048 kB
223
+VmLib: 1848 kB
224
+VmPTE: 240 kB
225
+VmSwap: 0 kB
226
+```
227
+
228
+**11.0692 kB virtual, 10.0056 kB resident per histogram.**
229
+
230
+50000 histograms with an exponentially-decaying sample size of 1028 and alpha of 0.015
231
+--------------------------------------------------------------------------------------
232
+
233
+```
234
+VmPeak: 557264 kB
235
+VmSize: 557264 kB
236
+VmLck: 0 kB
237
+VmHWM: 501056 kB
238
+VmRSS: 501056 kB
239
+VmData: 550092 kB
240
+VmStk: 136 kB
241
+VmExe: 1048 kB
242
+VmLib: 1848 kB
243
+VmPTE: 1032 kB
244
+VmSwap: 0 kB
245
+```
246
+
247
+**10.2932 kB virtual, 9.99872 kB resident per histogram.**
248
+
249
+1000 meters
250
+-----------
251
+
252
+```
253
+VmPeak: 74504 kB
254
+VmSize: 74504 kB
255
+VmLck: 0 kB
256
+VmHWM: 24124 kB
257
+VmRSS: 24124 kB
258
+VmData: 67340 kB
259
+VmStk: 136 kB
260
+VmExe: 1040 kB
261
+VmLib: 1848 kB
262
+VmPTE: 92 kB
263
+VmSwap: 0 kB
264
+```
265
+
266
+**31.9 kB virtual, 23.004 kB resident per meter.**
267
+
268
+10000 meters
269
+------------
270
+
271
+```
272
+VmPeak: 278920 kB
273
+VmSize: 278920 kB
274
+VmLck: 0 kB
275
+VmHWM: 227300 kB
276
+VmRSS: 227300 kB
277
+VmData: 271756 kB
278
+VmStk: 136 kB
279
+VmExe: 1040 kB
280
+VmLib: 1848 kB
281
+VmPTE: 488 kB
282
+VmSwap: 0 kB
283
+```
284
+
285
+**23.6316 kB virtual, 22.618 kB resident per meter.**
Godeps/_workspace/src/github.com/whyrusleeping/go-metrics/meter.go
new
+255
@@ -0,0 +1,255 @@
1
+package metrics
2
+
3
+import (
4
+ "sync"
5
+ "time"
6
+)
7
+
8
+// Meters count events to produce exponentially-weighted moving average rates
9
+// at one-, five-, and fifteen-minutes and a mean rate.
10
+type Meter interface {
11
+ Count() int64
12
+ Mark(int64)
13
+ RateFine() float64
14
+ Rate1() float64
15
+ Rate5() float64
16
+ Rate15() float64
17
+ RateMean() float64
18
+ Snapshot() Meter
19
+}
20
+
21
+// GetOrRegisterMeter returns an existing Meter or constructs and registers a
22
+// new StandardMeter.
23
+func GetOrRegisterMeter(name string, r Registry) Meter {
24
+ if nil == r {
25
+ r = DefaultRegistry
26
+ }
27
+ return r.GetOrRegister(name, NewMeter).(Meter)
28
+}
29
+
30
+// NewMeter constructs a new StandardMeter and launches a goroutine.
31
+func NewMeter() Meter {
32
+ if UseNilMetrics {
33
+ return NilMeter{}
34
+ }
35
+ m := newStandardMeter()
36
+ arbiter.Lock()
37
+ defer arbiter.Unlock()
38
+ arbiter.meters = append(arbiter.meters, m)
39
+ if !arbiter.started {
40
+ arbiter.started = true
41
+ go arbiter.tick()
42
+ }
43
+ return m
44
+}
45
+
46
+// NewMeter constructs and registers a new StandardMeter and launches a
47
+// goroutine.
48
+func NewRegisteredMeter(name string, r Registry) Meter {
49
+ c := NewMeter()
50
+ if nil == r {
51
+ r = DefaultRegistry
52
+ }
53
+ r.Register(name, c)
54
+ return c
55
+}
56
+
57
+// MeterSnapshot is a read-only copy of another Meter.
58
+type MeterSnapshot struct {
59
+ count int64
60
+ rateFine float64
61
+ rate1, rate5, rate15, rateMean float64
62
+}
63
+
64
+// Count returns the count of events at the time the snapshot was taken.
65
+func (m *MeterSnapshot) Count() int64 { return m.count }
66
+
67
+// Mark panics.
68
+func (*MeterSnapshot) Mark(n int64) {
69
+ panic("Mark called on a MeterSnapshot")
70
+}
71
+
72
+// RateFine returns the one-second moving average rate of events per second at the
73
+// time the snapshot was taken.
74
+func (m *MeterSnapshot) RateFine() float64 { return m.rateFine }
75
+
76
+// Rate1 returns the one-minute moving average rate of events per second at the
77
+// time the snapshot was taken.
78
+func (m *MeterSnapshot) Rate1() float64 { return m.rate1 }
79
+
80
+// Rate5 returns the five-minute moving average rate of events per second at
81
+// the time the snapshot was taken.
82
+func (m *MeterSnapshot) Rate5() float64 { return m.rate5 }
83
+
84
+// Rate15 returns the fifteen-minute moving average rate of events per second
85
+// at the time the snapshot was taken.
86
+func (m *MeterSnapshot) Rate15() float64 { return m.rate15 }
87
+
88
+// RateMean returns the meter's mean rate of events per second at the time the
89
+// snapshot was taken.
90
+func (m *MeterSnapshot) RateMean() float64 { return m.rateMean }
91
+
92
+// Snapshot returns the snapshot.
93
+func (m *MeterSnapshot) Snapshot() Meter { return m }
94
+
95
+// NilMeter is a no-op Meter.
96
+type NilMeter struct{}
97
+
98
+// Count is a no-op.
99
+func (NilMeter) Count() int64 { return 0 }
100
+
101
+// Mark is a no-op.
102
+func (NilMeter) Mark(n int64) {}
103
+
104
+// RateFine is a no-op.
105
+func (NilMeter) RateFine() float64 { return 0.0 }
106
+
107
+// Rate1 is a no-op.
108
+func (NilMeter) Rate1() float64 { return 0.0 }
109
+
110
+// Rate5 is a no-op.
111
+func (NilMeter) Rate5() float64 { return 0.0 }
112
+
113
+// Rate15is a no-op.
114
+func (NilMeter) Rate15() float64 { return 0.0 }
115
+
116
+// RateMean is a no-op.
117
+func (NilMeter) RateMean() float64 { return 0.0 }
118
+
119
+// Snapshot is a no-op.
120
+func (NilMeter) Snapshot() Meter { return NilMeter{} }
121
+
122
+// StandardMeter is the standard implementation of a Meter.
123
+type StandardMeter struct {
124
+ lock sync.RWMutex
125
+ snapshot *MeterSnapshot
126
+ aFine EWMA
127
+ a1, a5, a15 EWMA
128
+ startTime time.Time
129
+}
130
+
131
+func newStandardMeter() *StandardMeter {
132
+ return &StandardMeter{
133
+ snapshot: &MeterSnapshot{},
134
+ aFine: NewEWMAFine(),
135
+ a1: NewEWMA1(),
136
+ a5: NewEWMA5(),
137
+ a15: NewEWMA15(),
138
+ startTime: time.Now(),
139
+ }
140
+}
141
+
142
+// Count returns the number of events recorded.
143
+func (m *StandardMeter) Count() int64 {
144
+ m.lock.RLock()
145
+ count := m.snapshot.count
146
+ m.lock.RUnlock()
147
+ return count
148
+}
149
+
150
+// Mark records the occurance of n events.
151
+func (m *StandardMeter) Mark(n int64) {
152
+ m.lock.Lock()
153
+ defer m.lock.Unlock()
154
+ m.snapshot.count += n
155
+ m.aFine.Update(n)
156
+ m.a1.Update(n)
157
+ m.a5.Update(n)
158
+ m.a15.Update(n)
159
+ m.updateSnapshot()
160
+}
161
+
162
+// Rate1 returns the one-minute moving average rate of events per second.
163
+func (m *StandardMeter) RateFine() float64 {
164
+ m.lock.RLock()
165
+ rateFine := m.snapshot.rateFine
166
+ m.lock.RUnlock()
167
+ return rateFine
168
+}
169
+
170
+// Rate1 returns the one-minute moving average rate of events per second.
171
+func (m *StandardMeter) Rate1() float64 {
172
+ m.lock.RLock()
173
+ rate1 := m.snapshot.rate1
174
+ m.lock.RUnlock()
175
+ return rate1
176
+}
177
+
178
+// Rate5 returns the five-minute moving average rate of events per second.
179
+func (m *StandardMeter) Rate5() float64 {
180
+ m.lock.RLock()
181
+ rate5 := m.snapshot.rate5
182
+ m.lock.RUnlock()
183
+ return rate5
184
+}
185
+
186
+// Rate15 returns the fifteen-minute moving average rate of events per second.
187
+func (m *StandardMeter) Rate15() float64 {
188
+ m.lock.RLock()
189
+ rate15 := m.snapshot.rate15
190
+ m.lock.RUnlock()
191
+ return rate15
192
+}
193
+
194
+// RateMean returns the meter's mean rate of events per second.
195
+func (m *StandardMeter) RateMean() float64 {
196
+ m.lock.RLock()
197
+ rateMean := m.snapshot.rateMean
198
+ m.lock.RUnlock()
199
+ return rateMean
200
+}
201
+
202
+// Snapshot returns a read-only copy of the meter.
203
+func (m *StandardMeter) Snapshot() Meter {
204
+ m.lock.RLock()
205
+ snapshot := *m.snapshot
206
+ m.lock.RUnlock()
207
+ return &snapshot
208
+}
209
+
210
+func (m *StandardMeter) updateSnapshot() {
211
+ // should run with write lock held on m.lock
212
+ snapshot := m.snapshot
213
+ snapshot.rateFine = m.aFine.Rate()
214
+ snapshot.rate1 = m.a1.Rate()
215
+ snapshot.rate5 = m.a5.Rate()
216
+ snapshot.rate15 = m.a15.Rate()
217
+ snapshot.rateMean = float64(snapshot.count) / time.Since(m.startTime).Seconds()
218
+}
219
+
220
+func (m *StandardMeter) tick() {
221
+ m.lock.Lock()
222
+ defer m.lock.Unlock()
223
+ m.aFine.Tick()
224
+ m.a1.Tick()
225
+ m.a5.Tick()
226
+ m.a15.Tick()
227
+ m.updateSnapshot()
228
+}
229
+
230
+type meterArbiter struct {
231
+ sync.RWMutex
232
+ started bool
233
+ meters []*StandardMeter
234
+ ticker *time.Ticker
235
+}
236
+
237
+var arbiter = meterArbiter{ticker: time.NewTicker(time.Second)}
238
+
239
+// Ticks meters on the scheduled interval
240
+func (ma *meterArbiter) tick() {
241
+ for {
242
+ select {
243
+ case <-ma.ticker.C:
244
+ ma.tickMeters()
245
+ }
246
+ }
247
+}
248
+
249
+func (ma *meterArbiter) tickMeters() {
250
+ ma.RLock()
251
+ defer ma.RUnlock()
252
+ for _, meter := range ma.meters {
253
+ meter.tick()
254
+ }
255
+}
Godeps/_workspace/src/github.com/whyrusleeping/go-metrics/meter_test.go
new
+60
@@ -0,0 +1,60 @@
1
+package metrics
2
+
3
+import (
4
+ "testing"
5
+ "time"
6
+)
7
+
8
+func BenchmarkMeter(b *testing.B) {
9
+ m := NewMeter()
10
+ b.ResetTimer()
11
+ for i := 0; i < b.N; i++ {
12
+ m.Mark(1)
13
+ }
14
+}
15
+
16
+func TestGetOrRegisterMeter(t *testing.T) {
17
+ r := NewRegistry()
18
+ NewRegisteredMeter("foo", r).Mark(47)
19
+ if m := GetOrRegisterMeter("foo", r); 47 != m.Count() {
20
+ t.Fatal(m)
21
+ }
22
+}
23
+
24
+func TestMeterDecay(t *testing.T) {
25
+ ma := meterArbiter{
26
+ ticker: time.NewTicker(1),
27
+ }
28
+ m := newStandardMeter()
29
+ ma.meters = append(ma.meters, m)
30
+ go ma.tick()
31
+ m.Mark(1)
32
+ rateMean := m.RateMean()
33
+ time.Sleep(1)
34
+ if m.RateMean() >= rateMean {
35
+ t.Error("m.RateMean() didn't decrease")
36
+ }
37
+}
38
+
39
+func TestMeterNonzero(t *testing.T) {
40
+ m := NewMeter()
41
+ m.Mark(3)
42
+ if count := m.Count(); 3 != count {
43
+ t.Errorf("m.Count(): 3 != %v\n", count)
44
+ }
45
+}
46
+
47
+func TestMeterSnapshot(t *testing.T) {
48
+ m := NewMeter()
49
+ m.Mark(1)
50
+ if snapshot := m.Snapshot(); m.RateMean() != snapshot.RateMean() {
51
+ t.Fatal(snapshot)
52
+ }
53
+}
54
+
55
+func TestMeterZero(t *testing.T) {
56
+ m := NewMeter()
57
+ if count := m.Count(); 0 != count {
58
+ t.Errorf("m.Count(): 0 != %v\n", count)
59
+ }
60
+}
Godeps/_workspace/src/github.com/whyrusleeping/go-metrics/metrics.go
new
+13
@@ -0,0 +1,13 @@
1
+// Go port of Coda Hale's Metrics library
2
+//
3
+// <https://github.com/rcrowley/go-metrics>
4
+//
5
+// Coda Hale's original work: <https://github.com/codahale/metrics>
6
+package metrics
7
+
8
+// UseNilMetrics is checked by the constructor functions for all of the
9
+// standard metrics. If it is true, the metric returned is a stub.
10
+//
11
+// This global kill-switch helps quantify the observer effect and makes
12
+// for less cluttered pprof profiles.
13
+var UseNilMetrics bool = false
Godeps/_workspace/src/github.com/whyrusleeping/go-metrics/metrics_test.go
new
+107
@@ -0,0 +1,107 @@
1
+package metrics
2
+
3
+import (
4
+ "io/ioutil"
5
+ "log"
6
+ "sync"
7
+ "testing"
8
+)
9
+
10
+const FANOUT = 128
11
+
12
+// Stop the compiler from complaining during debugging.
13
+var (
14
+ _ = ioutil.Discard
15
+ _ = log.LstdFlags
16
+)
17
+
18
+func BenchmarkMetrics(b *testing.B) {
19
+ r := NewRegistry()
20
+ c := NewRegisteredCounter("counter", r)
21
+ g := NewRegisteredGauge("gauge", r)
22
+ gf := NewRegisteredGaugeFloat64("gaugefloat64", r)
23
+ h := NewRegisteredHistogram("histogram", r, NewUniformSample(100))
24
+ m := NewRegisteredMeter("meter", r)
25
+ t := NewRegisteredTimer("timer", r)
26
+ RegisterDebugGCStats(r)
27
+ RegisterRuntimeMemStats(r)
28
+ b.ResetTimer()
29
+ ch := make(chan bool)
30
+
31
+ wgD := &sync.WaitGroup{}
32
+ /*
33
+ wgD.Add(1)
34
+ go func() {
35
+ defer wgD.Done()
36
+ //log.Println("go CaptureDebugGCStats")
37
+ for {
38
+ select {
39
+ case <-ch:
40
+ //log.Println("done CaptureDebugGCStats")
41
+ return
42
+ default:
43
+ CaptureDebugGCStatsOnce(r)
44
+ }
45
+ }
46
+ }()
47
+ //*/
48
+
49
+ wgR := &sync.WaitGroup{}
50
+ //*
51
+ wgR.Add(1)
52
+ go func() {
53
+ defer wgR.Done()
54
+ //log.Println("go CaptureRuntimeMemStats")
55
+ for {
56
+ select {
57
+ case <-ch:
58
+ //log.Println("done CaptureRuntimeMemStats")
59
+ return
60
+ default:
61
+ CaptureRuntimeMemStatsOnce(r)
62
+ }
63
+ }
64
+ }()
65
+ //*/
66
+
67
+ wgW := &sync.WaitGroup{}
68
+ /*
69
+ wgW.Add(1)
70
+ go func() {
71
+ defer wgW.Done()
72
+ //log.Println("go Write")
73
+ for {
74
+ select {
75
+ case <-ch:
76
+ //log.Println("done Write")
77
+ return
78
+ default:
79
+ WriteOnce(r, ioutil.Discard)
80
+ }
81
+ }
82
+ }()
83
+ //*/
84
+
85
+ wg := &sync.WaitGroup{}
86
+ wg.Add(FANOUT)
87
+ for i := 0; i < FANOUT; i++ {
88
+ go func(i int) {
89
+ defer wg.Done()
90
+ //log.Println("go", i)
91
+ for i := 0; i < b.N; i++ {
92
+ c.Inc(1)
93
+ g.Update(int64(i))
94
+ gf.Update(float64(i))
95
+ h.Update(int64(i))
96
+ m.Mark(1)
97
+ t.Update(1)
98
+ }
99
+ //log.Println("done", i)
100
+ }(i)
101
+ }
102
+ wg.Wait()
103
+ close(ch)
104
+ wgD.Wait()
105
+ wgR.Wait()
106
+ wgW.Wait()
107
+}
Godeps/_workspace/src/github.com/whyrusleeping/go-metrics/opentsdb.go
new
+119
@@ -0,0 +1,119 @@
1
+package metrics
2
+
3
+import (
4
+ "bufio"
5
+ "fmt"
6
+ "log"
7
+ "net"
8
+ "os"
9
+ "strings"
10
+ "time"
11
+)
12
+
13
+var shortHostName string = ""
14
+
15
+// OpenTSDBConfig provides a container with configuration parameters for
16
+// the OpenTSDB exporter
17
+type OpenTSDBConfig struct {
18
+ Addr *net.TCPAddr // Network address to connect to
19
+ Registry Registry // Registry to be exported
20
+ FlushInterval time.Duration // Flush interval
21
+ DurationUnit time.Duration // Time conversion unit for durations
22
+ Prefix string // Prefix to be prepended to metric names
23
+}
24
+
25
+// OpenTSDB is a blocking exporter function which reports metrics in r
26
+// to a TSDB server located at addr, flushing them every d duration
27
+// and prepending metric names with prefix.
28
+func OpenTSDB(r Registry, d time.Duration, prefix string, addr *net.TCPAddr) {
29
+ OpenTSDBWithConfig(OpenTSDBConfig{
30
+ Addr: addr,
31
+ Registry: r,
32
+ FlushInterval: d,
33
+ DurationUnit: time.Nanosecond,
34
+ Prefix: prefix,
35
+ })
36
+}
37
+
38
+// OpenTSDBWithConfig is a blocking exporter function just like OpenTSDB,
39
+// but it takes a OpenTSDBConfig instead.
40
+func OpenTSDBWithConfig(c OpenTSDBConfig) {
41
+ for _ = range time.Tick(c.FlushInterval) {
42
+ if err := openTSDB(&c); nil != err {
43
+ log.Println(err)
44
+ }
45
+ }
46
+}
47
+
48
+func getShortHostname() string {
49
+ if shortHostName == "" {
50
+ host, _ := os.Hostname()
51
+ if index := strings.Index(host, "."); index > 0 {
52
+ shortHostName = host[:index]
53
+ } else {
54
+ shortHostName = host
55
+ }
56
+ }
57
+ return shortHostName
58
+}
59
+
60
+func openTSDB(c *OpenTSDBConfig) error {
61
+ shortHostname := getShortHostname()
62
+ now := time.Now().Unix()
63
+ du := float64(c.DurationUnit)
64
+ conn, err := net.DialTCP("tcp", nil, c.Addr)
65
+ if nil != err {
66
+ return err
67
+ }
68
+ defer conn.Close()
69
+ w := bufio.NewWriter(conn)
70
+ c.Registry.Each(func(name string, i interface{}) {
71
+ switch metric := i.(type) {
72
+ case Counter:
73
+ fmt.Fprintf(w, "put %s.%s.count %d %d host=%s\n", c.Prefix, name, now, metric.Count(), shortHostname)
74
+ case Gauge:
75
+ fmt.Fprintf(w, "put %s.%s.value %d %d host=%s\n", c.Prefix, name, now, metric.Value(), shortHostname)
76
+ case GaugeFloat64:
77
+ fmt.Fprintf(w, "put %s.%s.value %d %f host=%s\n", c.Prefix, name, now, metric.Value(), shortHostname)
78
+ case Histogram:
79
+ h := metric.Snapshot()
80
+ ps := h.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999})
81
+ fmt.Fprintf(w, "put %s.%s.count %d %d host=%s\n", c.Prefix, name, now, h.Count(), shortHostname)
82
+ fmt.Fprintf(w, "put %s.%s.min %d %d host=%s\n", c.Prefix, name, now, h.Min(), shortHostname)
83
+ fmt.Fprintf(w, "put %s.%s.max %d %d host=%s\n", c.Prefix, name, now, h.Max(), shortHostname)
84
+ fmt.Fprintf(w, "put %s.%s.mean %d %.2f host=%s\n", c.Prefix, name, now, h.Mean(), shortHostname)
85
+ fmt.Fprintf(w, "put %s.%s.std-dev %d %.2f host=%s\n", c.Prefix, name, now, h.StdDev(), shortHostname)
86
+ fmt.Fprintf(w, "put %s.%s.50-percentile %d %.2f host=%s\n", c.Prefix, name, now, ps[0], shortHostname)
87
+ fmt.Fprintf(w, "put %s.%s.75-percentile %d %.2f host=%s\n", c.Prefix, name, now, ps[1], shortHostname)
88
+ fmt.Fprintf(w, "put %s.%s.95-percentile %d %.2f host=%s\n", c.Prefix, name, now, ps[2], shortHostname)
89
+ fmt.Fprintf(w, "put %s.%s.99-percentile %d %.2f host=%s\n", c.Prefix, name, now, ps[3], shortHostname)
90
+ fmt.Fprintf(w, "put %s.%s.999-percentile %d %.2f host=%s\n", c.Prefix, name, now, ps[4], shortHostname)
91
+ case Meter:
92
+ m := metric.Snapshot()
93
+ fmt.Fprintf(w, "put %s.%s.count %d %d host=%s\n", c.Prefix, name, now, m.Count(), shortHostname)
94
+ fmt.Fprintf(w, "put %s.%s.one-minute %d %.2f host=%s\n", c.Prefix, name, now, m.Rate1(), shortHostname)
95
+ fmt.Fprintf(w, "put %s.%s.five-minute %d %.2f host=%s\n", c.Prefix, name, now, m.Rate5(), shortHostname)
96
+ fmt.Fprintf(w, "put %s.%s.fifteen-minute %d %.2f host=%s\n", c.Prefix, name, now, m.Rate15(), shortHostname)
97
+ fmt.Fprintf(w, "put %s.%s.mean %d %.2f host=%s\n", c.Prefix, name, now, m.RateMean(), shortHostname)
98
+ case Timer:
99
+ t := metric.Snapshot()
100
+ ps := t.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999})
101
+ fmt.Fprintf(w, "put %s.%s.count %d %d host=%s\n", c.Prefix, name, now, t.Count(), shortHostname)
102
+ fmt.Fprintf(w, "put %s.%s.min %d %d host=%s\n", c.Prefix, name, now, t.Min()/int64(du), shortHostname)
103
+ fmt.Fprintf(w, "put %s.%s.max %d %d host=%s\n", c.Prefix, name, now, t.Max()/int64(du), shortHostname)
104
+ fmt.Fprintf(w, "put %s.%s.mean %d %.2f host=%s\n", c.Prefix, name, now, t.Mean()/du, shortHostname)
105
+ fmt.Fprintf(w, "put %s.%s.std-dev %d %.2f host=%s\n", c.Prefix, name, now, t.StdDev()/du, shortHostname)
106
+ fmt.Fprintf(w, "put %s.%s.50-percentile %d %.2f host=%s\n", c.Prefix, name, now, ps[0]/du, shortHostname)
107
+ fmt.Fprintf(w, "put %s.%s.75-percentile %d %.2f host=%s\n", c.Prefix, name, now, ps[1]/du, shortHostname)
108
+ fmt.Fprintf(w, "put %s.%s.95-percentile %d %.2f host=%s\n", c.Prefix, name, now, ps[2]/du, shortHostname)
109
+ fmt.Fprintf(w, "put %s.%s.99-percentile %d %.2f host=%s\n", c.Prefix, name, now, ps[3]/du, shortHostname)
110
+ fmt.Fprintf(w, "put %s.%s.999-percentile %d %.2f host=%s\n", c.Prefix, name, now, ps[4]/du, shortHostname)
111
+ fmt.Fprintf(w, "put %s.%s.one-minute %d %.2f host=%s\n", c.Prefix, name, now, t.Rate1(), shortHostname)
112
+ fmt.Fprintf(w, "put %s.%s.five-minute %d %.2f host=%s\n", c.Prefix, name, now, t.Rate5(), shortHostname)
113
+ fmt.Fprintf(w, "put %s.%s.fifteen-minute %d %.2f host=%s\n", c.Prefix, name, now, t.Rate15(), shortHostname)
114
+ fmt.Fprintf(w, "put %s.%s.mean-rate %d %.2f host=%s\n", c.Prefix, name, now, t.RateMean(), shortHostname)
115
+ }
116
+ w.Flush()
117
+ })
118
+ return nil
119
+}
Godeps/_workspace/src/github.com/whyrusleeping/go-metrics/opentsdb_test.go
new
+21
@@ -0,0 +1,21 @@
1
+package metrics
2
+
3
+import (
4
+ "net"
5
+ "time"
6
+)
7
+
8
+func ExampleOpenTSDB() {
9
+ addr, _ := net.ResolveTCPAddr("net", ":2003")
10
+ go OpenTSDB(DefaultRegistry, 1*time.Second, "some.prefix", addr)
11
+}
12
+
13
+func ExampleOpenTSDBWithConfig() {
14
+ addr, _ := net.ResolveTCPAddr("net", ":2003")
15
+ go OpenTSDBWithConfig(OpenTSDBConfig{
16
+ Addr: addr,
17
+ Registry: DefaultRegistry,
18
+ FlushInterval: 1 * time.Second,
19
+ DurationUnit: time.Millisecond,
20
+ })
21
+}
Godeps/_workspace/src/github.com/whyrusleeping/go-metrics/registry.go
new
+180
@@ -0,0 +1,180 @@
1
+package metrics
2
+
3
+import (
4
+ "fmt"
5
+ "reflect"
6
+ "sync"
7
+)
8
+
9
+// DuplicateMetric is the error returned by Registry.Register when a metric
10
+// already exists. If you mean to Register that metric you must first
11
+// Unregister the existing metric.
12
+type DuplicateMetric string
13
+
14
+func (err DuplicateMetric) Error() string {
15
+ return fmt.Sprintf("duplicate metric: %s", string(err))
16
+}
17
+
18
+// A Registry holds references to a set of metrics by name and can iterate
19
+// over them, calling callback functions provided by the user.
20
+//
21
+// This is an interface so as to encourage other structs to implement
22
+// the Registry API as appropriate.
23
+type Registry interface {
24
+
25
+ // Call the given function for each registered metric.
26
+ Each(func(string, interface{}))
27
+
28
+ // Get the metric by the given name or nil if none is registered.
29
+ Get(string) interface{}
30
+
31
+ // Gets an existing metric or registers the given one.
32
+ // The interface can be the metric to register if not found in registry,
33
+ // or a function returning the metric for lazy instantiation.
34
+ GetOrRegister(string, interface{}) interface{}
35
+
36
+ // Register the given metric under the given name.
37
+ Register(string, interface{}) error
38
+
39
+ // Run all registered healthchecks.
40
+ RunHealthchecks()
41
+
42
+ // Unregister the metric with the given name.
43
+ Unregister(string)
44
+
45
+ // Unregister all metrics. (Mostly for testing.)
46
+ UnregisterAll()
47
+}
48
+
49
+// The standard implementation of a Registry is a mutex-protected map
50
+// of names to metrics.
51
+type StandardRegistry struct {
52
+ metrics map[string]interface{}
53
+ mutex sync.Mutex
54
+}
55
+
56
+// Create a new registry.
57
+func NewRegistry() Registry {
58
+ return &StandardRegistry{metrics: make(map[string]interface{})}
59
+}
60
+
61
+// Call the given function for each registered metric.
62
+func (r *StandardRegistry) Each(f func(string, interface{})) {
63
+ for name, i := range r.registered() {
64
+ f(name, i)
65
+ }
66
+}
67
+
68
+// Get the metric by the given name or nil if none is registered.
69
+func (r *StandardRegistry) Get(name string) interface{} {
70
+ r.mutex.Lock()
71
+ defer r.mutex.Unlock()
72
+ return r.metrics[name]
73
+}
74
+
75
+// Gets an existing metric or creates and registers a new one. Threadsafe
76
+// alternative to calling Get and Register on failure.
77
+// The interface can be the metric to register if not found in registry,
78
+// or a function returning the metric for lazy instantiation.
79
+func (r *StandardRegistry) GetOrRegister(name string, i interface{}) interface{} {
80
+ r.mutex.Lock()
81
+ defer r.mutex.Unlock()
82
+ if metric, ok := r.metrics[name]; ok {
83
+ return metric
84
+ }
85
+ if v := reflect.ValueOf(i); v.Kind() == reflect.Func {
86
+ i = v.Call(nil)[0].Interface()
87
+ }
88
+ r.register(name, i)
89
+ return i
90
+}
91
+
92
+// Register the given metric under the given name. Returns a DuplicateMetric
93
+// if a metric by the given name is already registered.
94
+func (r *StandardRegistry) Register(name string, i interface{}) error {
95
+ r.mutex.Lock()
96
+ defer r.mutex.Unlock()
97
+ return r.register(name, i)
98
+}
99
+
100
+// Run all registered healthchecks.
101
+func (r *StandardRegistry) RunHealthchecks() {
102
+ r.mutex.Lock()
103
+ defer r.mutex.Unlock()
104
+ for _, i := range r.metrics {
105
+ if h, ok := i.(Healthcheck); ok {
106
+ h.Check()
107
+ }
108
+ }
109
+}
110
+
111
+// Unregister the metric with the given name.
112
+func (r *StandardRegistry) Unregister(name string) {
113
+ r.mutex.Lock()
114
+ defer r.mutex.Unlock()
115
+ delete(r.metrics, name)
116
+}
117
+
118
+// Unregister all metrics. (Mostly for testing.)
119
+func (r *StandardRegistry) UnregisterAll() {
120
+ r.mutex.Lock()
121
+ defer r.mutex.Unlock()
122
+ for name, _ := range r.metrics {
123
+ delete(r.metrics, name)
124
+ }
125
+}
126
+
127
+func (r *StandardRegistry) register(name string, i interface{}) error {
128
+ if _, ok := r.metrics[name]; ok {
129
+ return DuplicateMetric(name)
130
+ }
131
+ switch i.(type) {
132
+ case Counter, Gauge, GaugeFloat64, Healthcheck, Histogram, Meter, Timer:
133
+ r.metrics[name] = i
134
+ }
135
+ return nil
136
+}
137
+
138
+func (r *StandardRegistry) registered() map[string]interface{} {
139
+ metrics := make(map[string]interface{}, len(r.metrics))
140
+ r.mutex.Lock()
141
+ defer r.mutex.Unlock()
142
+ for name, i := range r.metrics {
143
+ metrics[name] = i
144
+ }
145
+ return metrics
146
+}
147
+
148
+var DefaultRegistry Registry = NewRegistry()
149
+
150
+// Call the given function for each registered metric.
151
+func Each(f func(string, interface{})) {
152
+ DefaultRegistry.Each(f)
153
+}
154
+
155
+// Get the metric by the given name or nil if none is registered.
156
+func Get(name string) interface{} {
157
+ return DefaultRegistry.Get(name)
158
+}
159
+
160
+// Gets an existing metric or creates and registers a new one. Threadsafe
161
+// alternative to calling Get and Register on failure.
162
+func GetOrRegister(name string, i interface{}) interface{} {
163
+ return DefaultRegistry.GetOrRegister(name, i)
164
+}
165
+
166
+// Register the given metric under the given name. Returns a DuplicateMetric
167
+// if a metric by the given name is already registered.
168
+func Register(name string, i interface{}) error {
169
+ return DefaultRegistry.Register(name, i)
170
+}
171
+
172
+// Run all registered healthchecks.
173
+func RunHealthchecks() {
174
+ DefaultRegistry.RunHealthchecks()
175
+}
176
+
177
+// Unregister the metric with the given name.
178
+func Unregister(name string) {
179
+ DefaultRegistry.Unregister(name)
180
+}
Godeps/_workspace/src/github.com/whyrusleeping/go-metrics/registry_test.go
new
+118
@@ -0,0 +1,118 @@
1
+package metrics
2
+
3
+import "testing"
4
+
5
+func BenchmarkRegistry(b *testing.B) {
6
+ r := NewRegistry()
7
+ r.Register("foo", NewCounter())
8
+ b.ResetTimer()
9
+ for i := 0; i < b.N; i++ {
10
+ r.Each(func(string, interface{}) {})
11
+ }
12
+}
13
+
14
+func TestRegistry(t *testing.T) {
15
+ r := NewRegistry()
16
+ r.Register("foo", NewCounter())
17
+ i := 0
18
+ r.Each(func(name string, iface interface{}) {
19
+ i++
20
+ if "foo" != name {
21
+ t.Fatal(name)
22
+ }
23
+ if _, ok := iface.(Counter); !ok {
24
+ t.Fatal(iface)
25
+ }
26
+ })
27
+ if 1 != i {
28
+ t.Fatal(i)
29
+ }
30
+ r.Unregister("foo")
31
+ i = 0
32
+ r.Each(func(string, interface{}) { i++ })
33
+ if 0 != i {
34
+ t.Fatal(i)
35
+ }
36
+}
37
+
38
+func TestRegistryDuplicate(t *testing.T) {
39
+ r := NewRegistry()
40
+ if err := r.Register("foo", NewCounter()); nil != err {
41
+ t.Fatal(err)
42
+ }
43
+ if err := r.Register("foo", NewGauge()); nil == err {
44
+ t.Fatal(err)
45
+ }
46
+ i := 0
47
+ r.Each(func(name string, iface interface{}) {
48
+ i++
49
+ if _, ok := iface.(Counter); !ok {
50
+ t.Fatal(iface)
51
+ }
52
+ })
53
+ if 1 != i {
54
+ t.Fatal(i)
55
+ }
56
+}
57
+
58
+func TestRegistryGet(t *testing.T) {
59
+ r := NewRegistry()
60
+ r.Register("foo", NewCounter())
61
+ if count := r.Get("foo").(Counter).Count(); 0 != count {
62
+ t.Fatal(count)
63
+ }
64
+ r.Get("foo").(Counter).Inc(1)
65
+ if count := r.Get("foo").(Counter).Count(); 1 != count {
66
+ t.Fatal(count)
67
+ }
68
+}
69
+
70
+func TestRegistryGetOrRegister(t *testing.T) {
71
+ r := NewRegistry()
72
+
73
+ // First metric wins with GetOrRegister
74
+ _ = r.GetOrRegister("foo", NewCounter())
75
+ m := r.GetOrRegister("foo", NewGauge())
76
+ if _, ok := m.(Counter); !ok {
77
+ t.Fatal(m)
78
+ }
79
+
80
+ i := 0
81
+ r.Each(func(name string, iface interface{}) {
82
+ i++
83
+ if name != "foo" {
84
+ t.Fatal(name)
85
+ }
86
+ if _, ok := iface.(Counter); !ok {
87
+ t.Fatal(iface)
88
+ }
89
+ })
90
+ if i != 1 {
91
+ t.Fatal(i)
92
+ }
93
+}
94
+
95
+func TestRegistryGetOrRegisterWithLazyInstantiation(t *testing.T) {
96
+ r := NewRegistry()
97
+
98
+ // First metric wins with GetOrRegister
99
+ _ = r.GetOrRegister("foo", NewCounter)
100
+ m := r.GetOrRegister("foo", NewGauge)
101
+ if _, ok := m.(Counter); !ok {
102
+ t.Fatal(m)
103
+ }
104
+
105
+ i := 0
106
+ r.Each(func(name string, iface interface{}) {
107
+ i++
108
+ if name != "foo" {
109
+ t.Fatal(name)
110
+ }
111
+ if _, ok := iface.(Counter); !ok {
112
+ t.Fatal(iface)
113
+ }
114
+ })
115
+ if i != 1 {
116
+ t.Fatal(i)
117
+ }
118
+}
Godeps/_workspace/src/github.com/whyrusleeping/go-metrics/runtime.go
new
+200
@@ -0,0 +1,200 @@
1
+package metrics
2
+
3
+import (
4
+ "runtime"
5
+ "time"
6
+)
7
+
8
+var (
9
+ memStats runtime.MemStats
10
+ runtimeMetrics struct {
11
+ MemStats struct {
12
+ Alloc Gauge
13
+ BuckHashSys Gauge
14
+ DebugGC Gauge
15
+ EnableGC Gauge
16
+ Frees Gauge
17
+ HeapAlloc Gauge
18
+ HeapIdle Gauge
19
+ HeapInuse Gauge
20
+ HeapObjects Gauge
21
+ HeapReleased Gauge
22
+ HeapSys Gauge
23
+ LastGC Gauge
24
+ Lookups Gauge
25
+ Mallocs Gauge
26
+ MCacheInuse Gauge
27
+ MCacheSys Gauge
28
+ MSpanInuse Gauge
29
+ MSpanSys Gauge
30
+ NextGC Gauge
31
+ NumGC Gauge
32
+ PauseNs Histogram
33
+ PauseTotalNs Gauge
34
+ StackInuse Gauge
35
+ StackSys Gauge
36
+ Sys Gauge
37
+ TotalAlloc Gauge
38
+ }
39
+ NumCgoCall Gauge
40
+ NumGoroutine Gauge
41
+ ReadMemStats Timer
42
+ }
43
+ frees uint64
44
+ lookups uint64
45
+ mallocs uint64
46
+ numGC uint32
47
+ numCgoCalls int64
48
+)
49
+
50
+// Capture new values for the Go runtime statistics exported in
51
+// runtime.MemStats. This is designed to be called as a goroutine.
52
+func CaptureRuntimeMemStats(r Registry, d time.Duration) {
53
+ for _ = range time.Tick(d) {
54
+ CaptureRuntimeMemStatsOnce(r)
55
+ }
56
+}
57
+
58
+// Capture new values for the Go runtime statistics exported in
59
+// runtime.MemStats. This is designed to be called in a background
60
+// goroutine. Giving a registry which has not been given to
61
+// RegisterRuntimeMemStats will panic.
62
+//
63
+// Be very careful with this because runtime.ReadMemStats calls the C
64
+// functions runtime·semacquire(&runtime·worldsema) and runtime·stoptheworld()
65
+// and that last one does what it says on the tin.
66
+func CaptureRuntimeMemStatsOnce(r Registry) {
67
+ t := time.Now()
68
+ runtime.ReadMemStats(&memStats) // This takes 50-200us.
69
+ runtimeMetrics.ReadMemStats.UpdateSince(t)
70
+
71
+ runtimeMetrics.MemStats.Alloc.Update(int64(memStats.Alloc))
72
+ runtimeMetrics.MemStats.BuckHashSys.Update(int64(memStats.BuckHashSys))
73
+ if memStats.DebugGC {
74
+ runtimeMetrics.MemStats.DebugGC.Update(1)
75
+ } else {
76
+ runtimeMetrics.MemStats.DebugGC.Update(0)
77
+ }
78
+ if memStats.EnableGC {
79
+ runtimeMetrics.MemStats.EnableGC.Update(1)
80
+ } else {
81
+ runtimeMetrics.MemStats.EnableGC.Update(0)
82
+ }
83
+
84
+ runtimeMetrics.MemStats.Frees.Update(int64(memStats.Frees - frees))
85
+ runtimeMetrics.MemStats.HeapAlloc.Update(int64(memStats.HeapAlloc))
86
+ runtimeMetrics.MemStats.HeapIdle.Update(int64(memStats.HeapIdle))
87
+ runtimeMetrics.MemStats.HeapInuse.Update(int64(memStats.HeapInuse))
88
+ runtimeMetrics.MemStats.HeapObjects.Update(int64(memStats.HeapObjects))
89
+ runtimeMetrics.MemStats.HeapReleased.Update(int64(memStats.HeapReleased))
90
+ runtimeMetrics.MemStats.HeapSys.Update(int64(memStats.HeapSys))
91
+ runtimeMetrics.MemStats.LastGC.Update(int64(memStats.LastGC))
92
+ runtimeMetrics.MemStats.Lookups.Update(int64(memStats.Lookups - lookups))
93
+ runtimeMetrics.MemStats.Mallocs.Update(int64(memStats.Mallocs - mallocs))
94
+ runtimeMetrics.MemStats.MCacheInuse.Update(int64(memStats.MCacheInuse))
95
+ runtimeMetrics.MemStats.MCacheSys.Update(int64(memStats.MCacheSys))
96
+ runtimeMetrics.MemStats.MSpanInuse.Update(int64(memStats.MSpanInuse))
97
+ runtimeMetrics.MemStats.MSpanSys.Update(int64(memStats.MSpanSys))
98
+ runtimeMetrics.MemStats.NextGC.Update(int64(memStats.NextGC))
99
+ runtimeMetrics.MemStats.NumGC.Update(int64(memStats.NumGC - numGC))
100
+
101
+ // <https://code.google.com/p/go/source/browse/src/pkg/runtime/mgc0.c>
102
+ i := numGC % uint32(len(memStats.PauseNs))
103
+ ii := memStats.NumGC % uint32(len(memStats.PauseNs))
104
+ if memStats.NumGC-numGC >= uint32(len(memStats.PauseNs)) {
105
+ for i = 0; i < uint32(len(memStats.PauseNs)); i++ {
106
+ runtimeMetrics.MemStats.PauseNs.Update(int64(memStats.PauseNs[i]))
107
+ }
108
+ } else {
109
+ if i > ii {
110
+ for ; i < uint32(len(memStats.PauseNs)); i++ {
111
+ runtimeMetrics.MemStats.PauseNs.Update(int64(memStats.PauseNs[i]))
112
+ }
113
+ i = 0
114
+ }
115
+ for ; i < ii; i++ {
116
+ runtimeMetrics.MemStats.PauseNs.Update(int64(memStats.PauseNs[i]))
117
+ }
118
+ }
119
+ frees = memStats.Frees
120
+ lookups = memStats.Lookups
121
+ mallocs = memStats.Mallocs
122
+ numGC = memStats.NumGC
123
+
124
+ runtimeMetrics.MemStats.PauseTotalNs.Update(int64(memStats.PauseTotalNs))
125
+ runtimeMetrics.MemStats.StackInuse.Update(int64(memStats.StackInuse))
126
+ runtimeMetrics.MemStats.StackSys.Update(int64(memStats.StackSys))
127
+ runtimeMetrics.MemStats.Sys.Update(int64(memStats.Sys))
128
+ runtimeMetrics.MemStats.TotalAlloc.Update(int64(memStats.TotalAlloc))
129
+
130
+ currentNumCgoCalls := numCgoCall()
131
+ runtimeMetrics.NumCgoCall.Update(currentNumCgoCalls - numCgoCalls)
132
+ numCgoCalls = currentNumCgoCalls
133
+
134
+ runtimeMetrics.NumGoroutine.Update(int64(runtime.NumGoroutine()))
135
+}
136
+
137
+// Register runtimeMetrics for the Go runtime statistics exported in runtime and
138
+// specifically runtime.MemStats. The runtimeMetrics are named by their
139
+// fully-qualified Go symbols, i.e. runtime.MemStats.Alloc.
140
+func RegisterRuntimeMemStats(r Registry) {
141
+ runtimeMetrics.MemStats.Alloc = NewGauge()
142
+ runtimeMetrics.MemStats.BuckHashSys = NewGauge()
143
+ runtimeMetrics.MemStats.DebugGC = NewGauge()
144
+ runtimeMetrics.MemStats.EnableGC = NewGauge()
145
+ runtimeMetrics.MemStats.Frees = NewGauge()
146
+ runtimeMetrics.MemStats.HeapAlloc = NewGauge()
147
+ runtimeMetrics.MemStats.HeapIdle = NewGauge()
148
+ runtimeMetrics.MemStats.HeapInuse = NewGauge()
149
+ runtimeMetrics.MemStats.HeapObjects = NewGauge()
150
+ runtimeMetrics.MemStats.HeapReleased = NewGauge()
151
+ runtimeMetrics.MemStats.HeapSys = NewGauge()
152
+ runtimeMetrics.MemStats.LastGC = NewGauge()
153
+ runtimeMetrics.MemStats.Lookups = NewGauge()
154
+ runtimeMetrics.MemStats.Mallocs = NewGauge()
155
+ runtimeMetrics.MemStats.MCacheInuse = NewGauge()
156
+ runtimeMetrics.MemStats.MCacheSys = NewGauge()
157
+ runtimeMetrics.MemStats.MSpanInuse = NewGauge()
158
+ runtimeMetrics.MemStats.MSpanSys = NewGauge()
159
+ runtimeMetrics.MemStats.NextGC = NewGauge()
160
+ runtimeMetrics.MemStats.NumGC = NewGauge()
161
+ runtimeMetrics.MemStats.PauseNs = NewHistogram(NewExpDecaySample(1028, 0.015))
162
+ runtimeMetrics.MemStats.PauseTotalNs = NewGauge()
163
+ runtimeMetrics.MemStats.StackInuse = NewGauge()
164
+ runtimeMetrics.MemStats.StackSys = NewGauge()
165
+ runtimeMetrics.MemStats.Sys = NewGauge()
166
+ runtimeMetrics.MemStats.TotalAlloc = NewGauge()
167
+ runtimeMetrics.NumCgoCall = NewGauge()
168
+ runtimeMetrics.NumGoroutine = NewGauge()
169
+ runtimeMetrics.ReadMemStats = NewTimer()
170
+
171
+ r.Register("runtime.MemStats.Alloc", runtimeMetrics.MemStats.Alloc)
172
+ r.Register("runtime.MemStats.BuckHashSys", runtimeMetrics.MemStats.BuckHashSys)
173
+ r.Register("runtime.MemStats.DebugGC", runtimeMetrics.MemStats.DebugGC)
174
+ r.Register("runtime.MemStats.EnableGC", runtimeMetrics.MemStats.EnableGC)
175
+ r.Register("runtime.MemStats.Frees", runtimeMetrics.MemStats.Frees)
176
+ r.Register("runtime.MemStats.HeapAlloc", runtimeMetrics.MemStats.HeapAlloc)
177
+ r.Register("runtime.MemStats.HeapIdle", runtimeMetrics.MemStats.HeapIdle)
178
+ r.Register("runtime.MemStats.HeapInuse", runtimeMetrics.MemStats.HeapInuse)
179
+ r.Register("runtime.MemStats.HeapObjects", runtimeMetrics.MemStats.HeapObjects)
180
+ r.Register("runtime.MemStats.HeapReleased", runtimeMetrics.MemStats.HeapReleased)
181
+ r.Register("runtime.MemStats.HeapSys", runtimeMetrics.MemStats.HeapSys)
182
+ r.Register("runtime.MemStats.LastGC", runtimeMetrics.MemStats.LastGC)
183
+ r.Register("runtime.MemStats.Lookups", runtimeMetrics.MemStats.Lookups)
184
+ r.Register("runtime.MemStats.Mallocs", runtimeMetrics.MemStats.Mallocs)
185
+ r.Register("runtime.MemStats.MCacheInuse", runtimeMetrics.MemStats.MCacheInuse)
186
+ r.Register("runtime.MemStats.MCacheSys", runtimeMetrics.MemStats.MCacheSys)
187
+ r.Register("runtime.MemStats.MSpanInuse", runtimeMetrics.MemStats.MSpanInuse)
188
+ r.Register("runtime.MemStats.MSpanSys", runtimeMetrics.MemStats.MSpanSys)
189
+ r.Register("runtime.MemStats.NextGC", runtimeMetrics.MemStats.NextGC)
190
+ r.Register("runtime.MemStats.NumGC", runtimeMetrics.MemStats.NumGC)
191
+ r.Register("runtime.MemStats.PauseNs", runtimeMetrics.MemStats.PauseNs)
192
+ r.Register("runtime.MemStats.PauseTotalNs", runtimeMetrics.MemStats.PauseTotalNs)
193
+ r.Register("runtime.MemStats.StackInuse", runtimeMetrics.MemStats.StackInuse)
194
+ r.Register("runtime.MemStats.StackSys", runtimeMetrics.MemStats.StackSys)
195
+ r.Register("runtime.MemStats.Sys", runtimeMetrics.MemStats.Sys)
196
+ r.Register("runtime.MemStats.TotalAlloc", runtimeMetrics.MemStats.TotalAlloc)
197
+ r.Register("runtime.NumCgoCall", runtimeMetrics.NumCgoCall)
198
+ r.Register("runtime.NumGoroutine", runtimeMetrics.NumGoroutine)
199
+ r.Register("runtime.ReadMemStats", runtimeMetrics.ReadMemStats)
200
+}
Godeps/_workspace/src/github.com/whyrusleeping/go-metrics/runtime_cgo.go
new
+10
@@ -0,0 +1,10 @@
1
+// +build cgo
2
+// +build !appengine
3
+
4
+package metrics
5
+
6
+import "runtime"
7
+
8
+func numCgoCall() int64 {
9
+ return runtime.NumCgoCall()
10
+}
Godeps/_workspace/src/github.com/whyrusleeping/go-metrics/runtime_no_cgo.go
new
+7
@@ -0,0 +1,7 @@
1
+// +build !cgo appengine
2
+
3
+package metrics
4
+
5
+func numCgoCall() int64 {
6
+ return 0
7
+}
Godeps/_workspace/src/github.com/whyrusleeping/go-metrics/runtime_test.go
new
+78
@@ -0,0 +1,78 @@
1
+package metrics
2
+
3
+import (
4
+ "runtime"
5
+ "testing"
6
+ "time"
7
+)
8
+
9
+func BenchmarkRuntimeMemStats(b *testing.B) {
10
+ r := NewRegistry()
11
+ RegisterRuntimeMemStats(r)
12
+ b.ResetTimer()
13
+ for i := 0; i < b.N; i++ {
14
+ CaptureRuntimeMemStatsOnce(r)
15
+ }
16
+}
17
+
18
+func TestRuntimeMemStats(t *testing.T) {
19
+ r := NewRegistry()
20
+ RegisterRuntimeMemStats(r)
21
+ CaptureRuntimeMemStatsOnce(r)
22
+ zero := runtimeMetrics.MemStats.PauseNs.Count() // Get a "zero" since GC may have run before these tests.
23
+ runtime.GC()
24
+ CaptureRuntimeMemStatsOnce(r)
25
+ if count := runtimeMetrics.MemStats.PauseNs.Count(); 1 != count-zero {
26
+ t.Fatal(count - zero)
27
+ }
28
+ runtime.GC()
29
+ runtime.GC()
30
+ CaptureRuntimeMemStatsOnce(r)
31
+ if count := runtimeMetrics.MemStats.PauseNs.Count(); 3 != count-zero {
32
+ t.Fatal(count - zero)
33
+ }
34
+ for i := 0; i < 256; i++ {
35
+ runtime.GC()
36
+ }
37
+ CaptureRuntimeMemStatsOnce(r)
38
+ if count := runtimeMetrics.MemStats.PauseNs.Count(); 259 != count-zero {
39
+ t.Fatal(count - zero)
40
+ }
41
+ for i := 0; i < 257; i++ {
42
+ runtime.GC()
43
+ }
44
+ CaptureRuntimeMemStatsOnce(r)
45
+ if count := runtimeMetrics.MemStats.PauseNs.Count(); 515 != count-zero { // We lost one because there were too many GCs between captures.
46
+ t.Fatal(count - zero)
47
+ }
48
+}
49
+
50
+func TestRuntimeMemStatsBlocking(t *testing.T) {
51
+ if g := runtime.GOMAXPROCS(0); g < 2 {
52
+ t.Skipf("skipping TestRuntimeMemStatsBlocking with GOMAXPROCS=%d\n", g)
53
+ }
54
+ ch := make(chan int)
55
+ go testRuntimeMemStatsBlocking(ch)
56
+ var memStats runtime.MemStats
57
+ t0 := time.Now()
58
+ runtime.ReadMemStats(&memStats)
59
+ t1 := time.Now()
60
+ t.Log("i++ during runtime.ReadMemStats:", <-ch)
61
+ go testRuntimeMemStatsBlocking(ch)
62
+ d := t1.Sub(t0)
63
+ t.Log(d)
64
+ time.Sleep(d)
65
+ t.Log("i++ during time.Sleep:", <-ch)
66
+}
67
+
68
+func testRuntimeMemStatsBlocking(ch chan int) {
69
+ i := 0
70
+ for {
71
+ select {
72
+ case ch <- i:
73
+ return
74
+ default:
75
+ i++
76
+ }
77
+ }
78
+}
Godeps/_workspace/src/github.com/whyrusleeping/go-metrics/sample.go
new
+602
@@ -0,0 +1,602 @@
1
+package metrics
2
+
3
+import (
4
+ "math"
5
+ "math/rand"
6
+ "sort"
7
+ "sync"
8
+ "time"
9
+)
10
+
11
+const rescaleThreshold = time.Hour
12
+
13
+// Samples maintain a statistically-significant selection of values from
14
+// a stream.
15
+type Sample interface {
16
+ Clear()
17
+ Count() int64
18
+ Max() int64
19
+ Mean() float64
20
+ Min() int64
21
+ Percentile(float64) float64
22
+ Percentiles([]float64) []float64
23
+ Size() int
24
+ Snapshot() Sample
25
+ StdDev() float64
26
+ Sum() int64
27
+ Update(int64)
28
+ Values() []int64
29
+ Variance() float64
30
+}
31
+
32
+// ExpDecaySample is an exponentially-decaying sample using a forward-decaying
33
+// priority reservoir. See Cormode et al's "Forward Decay: A Practical Time
34
+// Decay Model for Streaming Systems".
35
+//
36
+// <http://www.research.att.com/people/Cormode_Graham/library/publications/CormodeShkapenyukSrivastavaXu09.pdf>
37
+type ExpDecaySample struct {
38
+ alpha float64
39
+ count int64
40
+ mutex sync.Mutex
41
+ reservoirSize int
42
+ t0, t1 time.Time
43
+ values *expDecaySampleHeap
44
+}
45
+
46
+// NewExpDecaySample constructs a new exponentially-decaying sample with the
47
+// given reservoir size and alpha.
48
+func NewExpDecaySample(reservoirSize int, alpha float64) Sample {
49
+ if UseNilMetrics {
50
+ return NilSample{}
51
+ }
52
+ s := &ExpDecaySample{
53
+ alpha: alpha,
54
+ reservoirSize: reservoirSize,
55
+ t0: time.Now(),
56
+ values: newExpDecaySampleHeap(reservoirSize),
57
+ }
58
+ s.t1 = time.Now().Add(rescaleThreshold)
59
+ return s
60
+}
61
+
62
+// Clear clears all samples.
63
+func (s *ExpDecaySample) Clear() {
64
+ s.mutex.Lock()
65
+ defer s.mutex.Unlock()
66
+ s.count = 0
67
+ s.t0 = time.Now()
68
+ s.t1 = s.t0.Add(rescaleThreshold)
69
+ s.values = newExpDecaySampleHeap(s.reservoirSize)
70
+}
71
+
72
+// Count returns the number of samples recorded, which may exceed the
73
+// reservoir size.
74
+func (s *ExpDecaySample) Count() int64 {
75
+ s.mutex.Lock()
76
+ defer s.mutex.Unlock()
77
+ return s.count
78
+}
79
+
80
+// Max returns the maximum value in the sample, which may not be the maximum
81
+// value ever to be part of the sample.
82
+func (s *ExpDecaySample) Max() int64 {
83
+ return SampleMax(s.Values())
84
+}
85
+
86
+// Mean returns the mean of the values in the sample.
87
+func (s *ExpDecaySample) Mean() float64 {
88
+ return SampleMean(s.Values())
89
+}
90
+
91
+// Min returns the minimum value in the sample, which may not be the minimum
92
+// value ever to be part of the sample.
93
+func (s *ExpDecaySample) Min() int64 {
94
+ return SampleMin(s.Values())
95
+}
96
+
97
+// Percentile returns an arbitrary percentile of values in the sample.
98
+func (s *ExpDecaySample) Percentile(p float64) float64 {
99
+ return SamplePercentile(s.Values(), p)
100
+}
101
+
102
+// Percentiles returns a slice of arbitrary percentiles of values in the
103
+// sample.
104
+func (s *ExpDecaySample) Percentiles(ps []float64) []float64 {
105
+ return SamplePercentiles(s.Values(), ps)
106
+}
107
+
108
+// Size returns the size of the sample, which is at most the reservoir size.
109
+func (s *ExpDecaySample) Size() int {
110
+ s.mutex.Lock()
111
+ defer s.mutex.Unlock()
112
+ return s.values.Size()
113
+}
114
+
115
+// Snapshot returns a read-only copy of the sample.
116
+func (s *ExpDecaySample) Snapshot() Sample {
117
+ s.mutex.Lock()
118
+ defer s.mutex.Unlock()
119
+ vals := s.values.Values()
120
+ values := make([]int64, len(vals))
121
+ for i, v := range vals {
122
+ values[i] = v.v
123
+ }
124
+ return &SampleSnapshot{
125
+ count: s.count,
126
+ values: values,
127
+ }
128
+}
129
+
130
+// StdDev returns the standard deviation of the values in the sample.
131
+func (s *ExpDecaySample) StdDev() float64 {
132
+ return SampleStdDev(s.Values())
133
+}
134
+
135
+// Sum returns the sum of the values in the sample.
136
+func (s *ExpDecaySample) Sum() int64 {
137
+ return SampleSum(s.Values())
138
+}
139
+
140
+// Update samples a new value.
141
+func (s *ExpDecaySample) Update(v int64) {
142
+ s.update(time.Now(), v)
143
+}
144
+
145
+// Values returns a copy of the values in the sample.
146
+func (s *ExpDecaySample) Values() []int64 {
147
+ s.mutex.Lock()
148
+ defer s.mutex.Unlock()
149
+ vals := s.values.Values()
150
+ values := make([]int64, len(vals))
151
+ for i, v := range vals {
152
+ values[i] = v.v
153
+ }
154
+ return values
155
+}
156
+
157
+// Variance returns the variance of the values in the sample.
158
+func (s *ExpDecaySample) Variance() float64 {
159
+ return SampleVariance(s.Values())
160
+}
161
+
162
+// update samples a new value at a particular timestamp. This is a method all
163
+// its own to facilitate testing.
164
+func (s *ExpDecaySample) update(t time.Time, v int64) {
165
+ s.mutex.Lock()
166
+ defer s.mutex.Unlock()
167
+ s.count++
168
+ if s.values.Size() == s.reservoirSize {
169
+ s.values.Pop()
170
+ }
171
+ s.values.Push(expDecaySample{
172
+ k: math.Exp(t.Sub(s.t0).Seconds()*s.alpha) / rand.Float64(),
173
+ v: v,
174
+ })
175
+ if t.After(s.t1) {
176
+ values := s.values.Values()
177
+ t0 := s.t0
178
+ s.values = newExpDecaySampleHeap(s.reservoirSize)
179
+ s.t0 = t
180
+ s.t1 = s.t0.Add(rescaleThreshold)
181
+ for _, v := range values {
182
+ v.k = v.k * math.Exp(-s.alpha*float64(s.t0.Sub(t0)))
183
+ s.values.Push(v)
184
+ }
185
+ }
186
+}
187
+
188
+// NilSample is a no-op Sample.
189
+type NilSample struct{}
190
+
191
+// Clear is a no-op.
192
+func (NilSample) Clear() {}
193
+
194
+// Count is a no-op.
195
+func (NilSample) Count() int64 { return 0 }
196
+
197
+// Max is a no-op.
198
+func (NilSample) Max() int64 { return 0 }
199
+
200
+// Mean is a no-op.
201
+func (NilSample) Mean() float64 { return 0.0 }
202
+
203
+// Min is a no-op.
204
+func (NilSample) Min() int64 { return 0 }
205
+
206
+// Percentile is a no-op.
207
+func (NilSample) Percentile(p float64) float64 { return 0.0 }
208
+
209
+// Percentiles is a no-op.
210
+func (NilSample) Percentiles(ps []float64) []float64 {
211
+ return make([]float64, len(ps))
212
+}
213
+
214
+// Size is a no-op.
215
+func (NilSample) Size() int { return 0 }
216
+
217
+// Sample is a no-op.
218
+func (NilSample) Snapshot() Sample { return NilSample{} }
219
+
220
+// StdDev is a no-op.
221
+func (NilSample) StdDev() float64 { return 0.0 }
222
+
223
+// Sum is a no-op.
224
+func (NilSample) Sum() int64 { return 0 }
225
+
226
+// Update is a no-op.
227
+func (NilSample) Update(v int64) {}
228
+
229
+// Values is a no-op.
230
+func (NilSample) Values() []int64 { return []int64{} }
231
+
232
+// Variance is a no-op.
233
+func (NilSample) Variance() float64 { return 0.0 }
234
+
235
+// SampleMax returns the maximum value of the slice of int64.
236
+func SampleMax(values []int64) int64 {
237
+ if 0 == len(values) {
238
+ return 0
239
+ }
240
+ var max int64 = math.MinInt64
241
+ for _, v := range values {
242
+ if max < v {
243
+ max = v
244
+ }
245
+ }
246
+ return max
247
+}
248
+
249
+// SampleMean returns the mean value of the slice of int64.
250
+func SampleMean(values []int64) float64 {
251
+ if 0 == len(values) {
252
+ return 0.0
253
+ }
254
+ return float64(SampleSum(values)) / float64(len(values))
255
+}
256
+
257
+// SampleMin returns the minimum value of the slice of int64.
258
+func SampleMin(values []int64) int64 {
259
+ if 0 == len(values) {
260
+ return 0
261
+ }
262
+ var min int64 = math.MaxInt64
263
+ for _, v := range values {
264
+ if min > v {
265
+ min = v
266
+ }
267
+ }
268
+ return min
269
+}
270
+
271
+// SamplePercentiles returns an arbitrary percentile of the slice of int64.
272
+func SamplePercentile(values int64Slice, p float64) float64 {
273
+ return SamplePercentiles(values, []float64{p})[0]
274
+}
275
+
276
+// SamplePercentiles returns a slice of arbitrary percentiles of the slice of
277
+// int64.
278
+func SamplePercentiles(values int64Slice, ps []float64) []float64 {
279
+ scores := make([]float64, len(ps))
280
+ size := len(values)
281
+ if size > 0 {
282
+ sort.Sort(values)
283
+ for i, p := range ps {
284
+ pos := p * float64(size+1)
285
+ if pos < 1.0 {
286
+ scores[i] = float64(values[0])
287
+ } else if pos >= float64(size) {
288
+ scores[i] = float64(values[size-1])
289
+ } else {
290
+ lower := float64(values[int(pos)-1])
291
+ upper := float64(values[int(pos)])
292
+ scores[i] = lower + (pos-math.Floor(pos))*(upper-lower)
293
+ }
294
+ }
295
+ }
296
+ return scores
297
+}
298
+
299
+// SampleSnapshot is a read-only copy of another Sample.
300
+type SampleSnapshot struct {
301
+ count int64
302
+ values []int64
303
+}
304
+
305
+// Clear panics.
306
+func (*SampleSnapshot) Clear() {
307
+ panic("Clear called on a SampleSnapshot")
308
+}
309
+
310
+// Count returns the count of inputs at the time the snapshot was taken.
311
+func (s *SampleSnapshot) Count() int64 { return s.count }
312
+
313
+// Max returns the maximal value at the time the snapshot was taken.
314
+func (s *SampleSnapshot) Max() int64 { return SampleMax(s.values) }
315
+
316
+// Mean returns the mean value at the time the snapshot was taken.
317
+func (s *SampleSnapshot) Mean() float64 { return SampleMean(s.values) }
318
+
319
+// Min returns the minimal value at the time the snapshot was taken.
320
+func (s *SampleSnapshot) Min() int64 { return SampleMin(s.values) }
321
+
322
+// Percentile returns an arbitrary percentile of values at the time the
323
+// snapshot was taken.
324
+func (s *SampleSnapshot) Percentile(p float64) float64 {
325
+ return SamplePercentile(s.values, p)
326
+}
327
+
328
+// Percentiles returns a slice of arbitrary percentiles of values at the time
329
+// the snapshot was taken.
330
+func (s *SampleSnapshot) Percentiles(ps []float64) []float64 {
331
+ return SamplePercentiles(s.values, ps)
332
+}
333
+
334
+// Size returns the size of the sample at the time the snapshot was taken.
335
+func (s *SampleSnapshot) Size() int { return len(s.values) }
336
+
337
+// Snapshot returns the snapshot.
338
+func (s *SampleSnapshot) Snapshot() Sample { return s }
339
+
340
+// StdDev returns the standard deviation of values at the time the snapshot was
341
+// taken.
342
+func (s *SampleSnapshot) StdDev() float64 { return SampleStdDev(s.values) }
343
+
344
+// Sum returns the sum of values at the time the snapshot was taken.
345
+func (s *SampleSnapshot) Sum() int64 { return SampleSum(s.values) }
346
+
347
+// Update panics.
348
+func (*SampleSnapshot) Update(int64) {
349
+ panic("Update called on a SampleSnapshot")
350
+}
351
+
352
+// Values returns a copy of the values in the sample.
353
+func (s *SampleSnapshot) Values() []int64 {
354
+ values := make([]int64, len(s.values))
355
+ copy(values, s.values)
356
+ return values
357
+}
358
+
359
+// Variance returns the variance of values at the time the snapshot was taken.
360
+func (s *SampleSnapshot) Variance() float64 { return SampleVariance(s.values) }
361
+
362
+// SampleStdDev returns the standard deviation of the slice of int64.
363
+func SampleStdDev(values []int64) float64 {
364
+ return math.Sqrt(SampleVariance(values))
365
+}
366
+
367
+// SampleSum returns the sum of the slice of int64.
368
+func SampleSum(values []int64) int64 {
369
+ var sum int64
370
+ for _, v := range values {
371
+ sum += v
372
+ }
373
+ return sum
374
+}
375
+
376
+// SampleVariance returns the variance of the slice of int64.
377
+func SampleVariance(values []int64) float64 {
378
+ if 0 == len(values) {
379
+ return 0.0
380
+ }
381
+ m := SampleMean(values)
382
+ var sum float64
383
+ for _, v := range values {
384
+ d := float64(v) - m
385
+ sum += d * d
386
+ }
387
+ return sum / float64(len(values))
388
+}
389
+
390
+// A uniform sample using Vitter's Algorithm R.
391
+//
392
+// <http://www.cs.umd.edu/~samir/498/vitter.pdf>
393
+type UniformSample struct {
394
+ count int64
395
+ mutex sync.Mutex
396
+ reservoirSize int
397
+ values []int64
398
+}
399
+
400
+// NewUniformSample constructs a new uniform sample with the given reservoir
401
+// size.
402
+func NewUniformSample(reservoirSize int) Sample {
403
+ if UseNilMetrics {
404
+ return NilSample{}
405
+ }
406
+ return &UniformSample{
407
+ reservoirSize: reservoirSize,
408
+ values: make([]int64, 0, reservoirSize),
409
+ }
410
+}
411
+
412
+// Clear clears all samples.
413
+func (s *UniformSample) Clear() {
414
+ s.mutex.Lock()
415
+ defer s.mutex.Unlock()
416
+ s.count = 0
417
+ s.values = make([]int64, 0, s.reservoirSize)
418
+}
419
+
420
+// Count returns the number of samples recorded, which may exceed the
421
+// reservoir size.
422
+func (s *UniformSample) Count() int64 {
423
+ s.mutex.Lock()
424
+ defer s.mutex.Unlock()
425
+ return s.count
426
+}
427
+
428
+// Max returns the maximum value in the sample, which may not be the maximum
429
+// value ever to be part of the sample.
430
+func (s *UniformSample) Max() int64 {
431
+ s.mutex.Lock()
432
+ defer s.mutex.Unlock()
433
+ return SampleMax(s.values)
434
+}
435
+
436
+// Mean returns the mean of the values in the sample.
437
+func (s *UniformSample) Mean() float64 {
438
+ s.mutex.Lock()
439
+ defer s.mutex.Unlock()
440
+ return SampleMean(s.values)
441
+}
442
+
443
+// Min returns the minimum value in the sample, which may not be the minimum
444
+// value ever to be part of the sample.
445
+func (s *UniformSample) Min() int64 {
446
+ s.mutex.Lock()
447
+ defer s.mutex.Unlock()
448
+ return SampleMin(s.values)
449
+}
450
+
451
+// Percentile returns an arbitrary percentile of values in the sample.
452
+func (s *UniformSample) Percentile(p float64) float64 {
453
+ s.mutex.Lock()
454
+ defer s.mutex.Unlock()
455
+ return SamplePercentile(s.values, p)
456
+}
457
+
458
+// Percentiles returns a slice of arbitrary percentiles of values in the
459
+// sample.
460
+func (s *UniformSample) Percentiles(ps []float64) []float64 {
461
+ s.mutex.Lock()
462
+ defer s.mutex.Unlock()
463
+ return SamplePercentiles(s.values, ps)
464
+}
465
+
466
+// Size returns the size of the sample, which is at most the reservoir size.
467
+func (s *UniformSample) Size() int {
468
+ s.mutex.Lock()
469
+ defer s.mutex.Unlock()
470
+ return len(s.values)
471
+}
472
+
473
+// Snapshot returns a read-only copy of the sample.
474
+func (s *UniformSample) Snapshot() Sample {
475
+ s.mutex.Lock()
476
+ defer s.mutex.Unlock()
477
+ values := make([]int64, len(s.values))
478
+ copy(values, s.values)
479
+ return &SampleSnapshot{
480
+ count: s.count,
481
+ values: values,
482
+ }
483
+}
484
+
485
+// StdDev returns the standard deviation of the values in the sample.
486
+func (s *UniformSample) StdDev() float64 {
487
+ s.mutex.Lock()
488
+ defer s.mutex.Unlock()
489
+ return SampleStdDev(s.values)
490
+}
491
+
492
+// Sum returns the sum of the values in the sample.
493
+func (s *UniformSample) Sum() int64 {
494
+ s.mutex.Lock()
495
+ defer s.mutex.Unlock()
496
+ return SampleSum(s.values)
497
+}
498
+
499
+// Update samples a new value.
500
+func (s *UniformSample) Update(v int64) {
501
+ s.mutex.Lock()
502
+ defer s.mutex.Unlock()
503
+ s.count++
504
+ if len(s.values) < s.reservoirSize {
505
+ s.values = append(s.values, v)
506
+ } else {
507
+ s.values[rand.Intn(s.reservoirSize)] = v
508
+ }
509
+}
510
+
511
+// Values returns a copy of the values in the sample.
512
+func (s *UniformSample) Values() []int64 {
513
+ s.mutex.Lock()
514
+ defer s.mutex.Unlock()
515
+ values := make([]int64, len(s.values))
516
+ copy(values, s.values)
517
+ return values
518
+}
519
+
520
+// Variance returns the variance of the values in the sample.
521
+func (s *UniformSample) Variance() float64 {
522
+ s.mutex.Lock()
523
+ defer s.mutex.Unlock()
524
+ return SampleVariance(s.values)
525
+}
526
+
527
+// expDecaySample represents an individual sample in a heap.
528
+type expDecaySample struct {
529
+ k float64
530
+ v int64
531
+}
532
+
533
+func newExpDecaySampleHeap(reservoirSize int) *expDecaySampleHeap {
534
+ return &expDecaySampleHeap{make([]expDecaySample, 0, reservoirSize)}
535
+}
536
+
537
+// expDecaySampleHeap is a min-heap of expDecaySamples.
538
+// The internal implementation is copied from the standard library's container/heap
539
+type expDecaySampleHeap struct {
540
+ s []expDecaySample
541
+}
542
+
543
+func (h *expDecaySampleHeap) Push(s expDecaySample) {
544
+ n := len(h.s)
545
+ h.s = h.s[0 : n+1]
546
+ h.s[n] = s
547
+ h.up(n)
548
+}
549
+
550
+func (h *expDecaySampleHeap) Pop() expDecaySample {
551
+ n := len(h.s) - 1
552
+ h.s[0], h.s[n] = h.s[n], h.s[0]
553
+ h.down(0, n)
554
+
555
+ n = len(h.s)
556
+ s := h.s[n-1]
557
+ h.s = h.s[0 : n-1]
558
+ return s
559
+}
560
+
561
+func (h *expDecaySampleHeap) Size() int {
562
+ return len(h.s)
563
+}
564
+
565
+func (h *expDecaySampleHeap) Values() []expDecaySample {
566
+ return h.s
567
+}
568
+
569
+func (h *expDecaySampleHeap) up(j int) {
570
+ for {
571
+ i := (j - 1) / 2 // parent
572
+ if i == j || !(h.s[j].k < h.s[i].k) {
573
+ break
574
+ }
575
+ h.s[i], h.s[j] = h.s[j], h.s[i]
576
+ j = i
577
+ }
578
+}
579
+
580
+func (h *expDecaySampleHeap) down(i, n int) {
581
+ for {
582
+ j1 := 2*i + 1
583
+ if j1 >= n || j1 < 0 { // j1 < 0 after int overflow
584
+ break
585
+ }
586
+ j := j1 // left child
587
+ if j2 := j1 + 1; j2 < n && !(h.s[j1].k < h.s[j2].k) {
588
+ j = j2 // = 2*i + 2 // right child
589
+ }
590
+ if !(h.s[j].k < h.s[i].k) {
591
+ break
592
+ }
593
+ h.s[i], h.s[j] = h.s[j], h.s[i]
594
+ i = j
595
+ }
596
+}
597
+
598
+type int64Slice []int64
599
+
600
+func (p int64Slice) Len() int { return len(p) }
601
+func (p int64Slice) Less(i, j int) bool { return p[i] < p[j] }
602
+func (p int64Slice) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
Godeps/_workspace/src/github.com/whyrusleeping/go-metrics/sample_test.go
new
+352
@@ -0,0 +1,352 @@
1
+package metrics
2
+
3
+import (
4
+ "math/rand"
5
+ "runtime"
6
+ "testing"
7
+ "time"
8
+)
9
+
10
+// Benchmark{Compute,Copy}{1000,1000000} demonstrate that, even for relatively
11
+// expensive computations like Variance, the cost of copying the Sample, as
12
+// approximated by a make and copy, is much greater than the cost of the
13
+// computation for small samples and only slightly less for large samples.
14
+func BenchmarkCompute1000(b *testing.B) {
15
+ s := make([]int64, 1000)
16
+ for i := 0; i < len(s); i++ {
17
+ s[i] = int64(i)
18
+ }
19
+ b.ResetTimer()
20
+ for i := 0; i < b.N; i++ {
21
+ SampleVariance(s)
22
+ }
23
+}
24
+func BenchmarkCompute1000000(b *testing.B) {
25
+ s := make([]int64, 1000000)
26
+ for i := 0; i < len(s); i++ {
27
+ s[i] = int64(i)
28
+ }
29
+ b.ResetTimer()
30
+ for i := 0; i < b.N; i++ {
31
+ SampleVariance(s)
32
+ }
33
+}
34
+func BenchmarkCopy1000(b *testing.B) {
35
+ s := make([]int64, 1000)
36
+ for i := 0; i < len(s); i++ {
37
+ s[i] = int64(i)
38
+ }
39
+ b.ResetTimer()
40
+ for i := 0; i < b.N; i++ {
41
+ sCopy := make([]int64, len(s))
42
+ copy(sCopy, s)
43
+ }
44
+}
45
+func BenchmarkCopy1000000(b *testing.B) {
46
+ s := make([]int64, 1000000)
47
+ for i := 0; i < len(s); i++ {
48
+ s[i] = int64(i)
49
+ }
50
+ b.ResetTimer()
51
+ for i := 0; i < b.N; i++ {
52
+ sCopy := make([]int64, len(s))
53
+ copy(sCopy, s)
54
+ }
55
+}
56
+
57
+func BenchmarkExpDecaySample257(b *testing.B) {
58
+ benchmarkSample(b, NewExpDecaySample(257, 0.015))
59
+}
60
+
61
+func BenchmarkExpDecaySample514(b *testing.B) {
62
+ benchmarkSample(b, NewExpDecaySample(514, 0.015))
63
+}
64
+
65
+func BenchmarkExpDecaySample1028(b *testing.B) {
66
+ benchmarkSample(b, NewExpDecaySample(1028, 0.015))
67
+}
68
+
69
+func BenchmarkUniformSample257(b *testing.B) {
70
+ benchmarkSample(b, NewUniformSample(257))
71
+}
72
+
73
+func BenchmarkUniformSample514(b *testing.B) {
74
+ benchmarkSample(b, NewUniformSample(514))
75
+}
76
+
77
+func BenchmarkUniformSample1028(b *testing.B) {
78
+ benchmarkSample(b, NewUniformSample(1028))
79
+}
80
+
81
+func TestExpDecaySample10(t *testing.T) {
82
+ rand.Seed(1)
83
+ s := NewExpDecaySample(100, 0.99)
84
+ for i := 0; i < 10; i++ {
85
+ s.Update(int64(i))
86
+ }
87
+ if size := s.Count(); 10 != size {
88
+ t.Errorf("s.Count(): 10 != %v\n", size)
89
+ }
90
+ if size := s.Size(); 10 != size {
91
+ t.Errorf("s.Size(): 10 != %v\n", size)
92
+ }
93
+ if l := len(s.Values()); 10 != l {
94
+ t.Errorf("len(s.Values()): 10 != %v\n", l)
95
+ }
96
+ for _, v := range s.Values() {
97
+ if v > 10 || v < 0 {
98
+ t.Errorf("out of range [0, 10): %v\n", v)
99
+ }
100
+ }
101
+}
102
+
103
+func TestExpDecaySample100(t *testing.T) {
104
+ rand.Seed(1)
105
+ s := NewExpDecaySample(1000, 0.01)
106
+ for i := 0; i < 100; i++ {
107
+ s.Update(int64(i))
108
+ }
109
+ if size := s.Count(); 100 != size {
110
+ t.Errorf("s.Count(): 100 != %v\n", size)
111
+ }
112
+ if size := s.Size(); 100 != size {
113
+ t.Errorf("s.Size(): 100 != %v\n", size)
114
+ }
115
+ if l := len(s.Values()); 100 != l {
116
+ t.Errorf("len(s.Values()): 100 != %v\n", l)
117
+ }
118
+ for _, v := range s.Values() {
119
+ if v > 100 || v < 0 {
120
+ t.Errorf("out of range [0, 100): %v\n", v)
121
+ }
122
+ }
123
+}
124
+
125
+func TestExpDecaySample1000(t *testing.T) {
126
+ rand.Seed(1)
127
+ s := NewExpDecaySample(100, 0.99)
128
+ for i := 0; i < 1000; i++ {
129
+ s.Update(int64(i))
130
+ }
131
+ if size := s.Count(); 1000 != size {
132
+ t.Errorf("s.Count(): 1000 != %v\n", size)
133
+ }
134
+ if size := s.Size(); 100 != size {
135
+ t.Errorf("s.Size(): 100 != %v\n", size)
136
+ }
137
+ if l := len(s.Values()); 100 != l {
138
+ t.Errorf("len(s.Values()): 100 != %v\n", l)
139
+ }
140
+ for _, v := range s.Values() {
141
+ if v > 1000 || v < 0 {
142
+ t.Errorf("out of range [0, 1000): %v\n", v)
143
+ }
144
+ }
145
+}
146
+
147
+// This test makes sure that the sample's priority is not amplified by using
148
+// nanosecond duration since start rather than second duration since start.
149
+// The priority becomes +Inf quickly after starting if this is done,
150
+// effectively freezing the set of samples until a rescale step happens.
151
+func TestExpDecaySampleNanosecondRegression(t *testing.T) {
152
+ rand.Seed(1)
153
+ s := NewExpDecaySample(100, 0.99)
154
+ for i := 0; i < 100; i++ {
155
+ s.Update(10)
156
+ }
157
+ time.Sleep(1 * time.Millisecond)
158
+ for i := 0; i < 100; i++ {
159
+ s.Update(20)
160
+ }
161
+ v := s.Values()
162
+ avg := float64(0)
163
+ for i := 0; i < len(v); i++ {
164
+ avg += float64(v[i])
165
+ }
166
+ avg /= float64(len(v))
167
+ if avg > 16 || avg < 14 {
168
+ t.Errorf("out of range [14, 16]: %v\n", avg)
169
+ }
170
+}
171
+
172
+func TestExpDecaySampleSnapshot(t *testing.T) {
173
+ now := time.Now()
174
+ rand.Seed(1)
175
+ s := NewExpDecaySample(100, 0.99)
176
+ for i := 1; i <= 10000; i++ {
177
+ s.(*ExpDecaySample).update(now.Add(time.Duration(i)), int64(i))
178
+ }
179
+ snapshot := s.Snapshot()
180
+ s.Update(1)
181
+ testExpDecaySampleStatistics(t, snapshot)
182
+}
183
+
184
+func TestExpDecaySampleStatistics(t *testing.T) {
185
+ now := time.Now()
186
+ rand.Seed(1)
187
+ s := NewExpDecaySample(100, 0.99)
188
+ for i := 1; i <= 10000; i++ {
189
+ s.(*ExpDecaySample).update(now.Add(time.Duration(i)), int64(i))
190
+ }
191
+ testExpDecaySampleStatistics(t, s)
192
+}
193
+
194
+func TestUniformSample(t *testing.T) {
195
+ rand.Seed(1)
196
+ s := NewUniformSample(100)
197
+ for i := 0; i < 1000; i++ {
198
+ s.Update(int64(i))
199
+ }
200
+ if size := s.Count(); 1000 != size {
201
+ t.Errorf("s.Count(): 1000 != %v\n", size)
202
+ }
203
+ if size := s.Size(); 100 != size {
204
+ t.Errorf("s.Size(): 100 != %v\n", size)
205
+ }
206
+ if l := len(s.Values()); 100 != l {
207
+ t.Errorf("len(s.Values()): 100 != %v\n", l)
208
+ }
209
+ for _, v := range s.Values() {
210
+ if v > 1000 || v < 0 {
211
+ t.Errorf("out of range [0, 100): %v\n", v)
212
+ }
213
+ }
214
+}
215
+
216
+func TestUniformSampleIncludesTail(t *testing.T) {
217
+ rand.Seed(1)
218
+ s := NewUniformSample(100)
219
+ max := 100
220
+ for i := 0; i < max; i++ {
221
+ s.Update(int64(i))
222
+ }
223
+ v := s.Values()
224
+ sum := 0
225
+ exp := (max - 1) * max / 2
226
+ for i := 0; i < len(v); i++ {
227
+ sum += int(v[i])
228
+ }
229
+ if exp != sum {
230
+ t.Errorf("sum: %v != %v\n", exp, sum)
231
+ }
232
+}
233
+
234
+func TestUniformSampleSnapshot(t *testing.T) {
235
+ s := NewUniformSample(100)
236
+ for i := 1; i <= 10000; i++ {
237
+ s.Update(int64(i))
238
+ }
239
+ snapshot := s.Snapshot()
240
+ s.Update(1)
241
+ testUniformSampleStatistics(t, snapshot)
242
+}
243
+
244
+func TestUniformSampleStatistics(t *testing.T) {
245
+ rand.Seed(1)
246
+ s := NewUniformSample(100)
247
+ for i := 1; i <= 10000; i++ {
248
+ s.Update(int64(i))
249
+ }
250
+ testUniformSampleStatistics(t, s)
251
+}
252
+
253
+func benchmarkSample(b *testing.B, s Sample) {
254
+ var memStats runtime.MemStats
255
+ runtime.ReadMemStats(&memStats)
256
+ pauseTotalNs := memStats.PauseTotalNs
257
+ b.ResetTimer()
258
+ for i := 0; i < b.N; i++ {
259
+ s.Update(1)
260
+ }
261
+ b.StopTimer()
262
+ runtime.GC()
263
+ runtime.ReadMemStats(&memStats)
264
+ b.Logf("GC cost: %d ns/op", int(memStats.PauseTotalNs-pauseTotalNs)/b.N)
265
+}
266
+
267
+func testExpDecaySampleStatistics(t *testing.T, s Sample) {
268
+ if count := s.Count(); 10000 != count {
269
+ t.Errorf("s.Count(): 10000 != %v\n", count)
270
+ }
271
+ if min := s.Min(); 107 != min {
272
+ t.Errorf("s.Min(): 107 != %v\n", min)
273
+ }
274
+ if max := s.Max(); 10000 != max {
275
+ t.Errorf("s.Max(): 10000 != %v\n", max)
276
+ }
277
+ if mean := s.Mean(); 4965.98 != mean {
278
+ t.Errorf("s.Mean(): 4965.98 != %v\n", mean)
279
+ }
280
+ if stdDev := s.StdDev(); 2959.825156930727 != stdDev {
281
+ t.Errorf("s.StdDev(): 2959.825156930727 != %v\n", stdDev)
282
+ }
283
+ ps := s.Percentiles([]float64{0.5, 0.75, 0.99})
284
+ if 4615 != ps[0] {
285
+ t.Errorf("median: 4615 != %v\n", ps[0])
286
+ }
287
+ if 7672 != ps[1] {
288
+ t.Errorf("75th percentile: 7672 != %v\n", ps[1])
289
+ }
290
+ if 9998.99 != ps[2] {
291
+ t.Errorf("99th percentile: 9998.99 != %v\n", ps[2])
292
+ }
293
+}
294
+
295
+func testUniformSampleStatistics(t *testing.T, s Sample) {
296
+ if count := s.Count(); 10000 != count {
297
+ t.Errorf("s.Count(): 10000 != %v\n", count)
298
+ }
299
+ if min := s.Min(); 9412 != min {
300
+ t.Errorf("s.Min(): 9412 != %v\n", min)
301
+ }
302
+ if max := s.Max(); 10000 != max {
303
+ t.Errorf("s.Max(): 10000 != %v\n", max)
304
+ }
305
+ if mean := s.Mean(); 9902.26 != mean {
306
+ t.Errorf("s.Mean(): 9902.26 != %v\n", mean)
307
+ }
308
+ if stdDev := s.StdDev(); 101.8667384380201 != stdDev {
309
+ t.Errorf("s.StdDev(): 101.8667384380201 != %v\n", stdDev)
310
+ }
311
+ ps := s.Percentiles([]float64{0.5, 0.75, 0.99})
312
+ if 9930.5 != ps[0] {
313
+ t.Errorf("median: 9930.5 != %v\n", ps[0])
314
+ }
315
+ if 9973.75 != ps[1] {
316
+ t.Errorf("75th percentile: 9973.75 != %v\n", ps[1])
317
+ }
318
+ if 9999.99 != ps[2] {
319
+ t.Errorf("99th percentile: 9999.99 != %v\n", ps[2])
320
+ }
321
+}
322
+
323
+// TestUniformSampleConcurrentUpdateCount would expose data race problems with
324
+// concurrent Update and Count calls on Sample when test is called with -race
325
+// argument
326
+func TestUniformSampleConcurrentUpdateCount(t *testing.T) {
327
+ if testing.Short() {
328
+ t.Skip("skipping in short mode")
329
+ }
330
+ s := NewUniformSample(100)
331
+ for i := 0; i < 100; i++ {
332
+ s.Update(int64(i))
333
+ }
334
+ quit := make(chan struct{})
335
+ go func() {
336
+ t := time.NewTicker(10 * time.Millisecond)
337
+ for {
338
+ select {
339
+ case <-t.C:
340
+ s.Update(rand.Int63())
341
+ case <-quit:
342
+ t.Stop()
343
+ return
344
+ }
345
+ }
346
+ }()
347
+ for i := 0; i < 1000; i++ {
348
+ s.Count()
349
+ time.Sleep(5 * time.Millisecond)
350
+ }
351
+ quit <- struct{}{}
352
+}
Godeps/_workspace/src/github.com/whyrusleeping/go-metrics/stathat/stathat.go
new
+69
@@ -0,0 +1,69 @@
1
+// Metrics output to StatHat.
2
+package stathat
3
+
4
+import (
5
+ "github.com/rcrowley/go-metrics"
6
+ "github.com/stathat/go"
7
+ "log"
8
+ "time"
9
+)
10
+
11
+func Stathat(r metrics.Registry, d time.Duration, userkey string) {
12
+ for {
13
+ if err := sh(r, userkey); nil != err {
14
+ log.Println(err)
15
+ }
16
+ time.Sleep(d)
17
+ }
18
+}
19
+
20
+func sh(r metrics.Registry, userkey string) error {
21
+ r.Each(func(name string, i interface{}) {
22
+ switch metric := i.(type) {
23
+ case metrics.Counter:
24
+ stathat.PostEZCount(name, userkey, int(metric.Count()))
25
+ case metrics.Gauge:
26
+ stathat.PostEZValue(name, userkey, float64(metric.Value()))
27
+ case metrics.GaugeFloat64:
28
+ stathat.PostEZValue(name, userkey, float64(metric.Value()))
29
+ case metrics.Histogram:
30
+ h := metric.Snapshot()
31
+ ps := h.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999})
32
+ stathat.PostEZCount(name+".count", userkey, int(h.Count()))
33
+ stathat.PostEZValue(name+".min", userkey, float64(h.Min()))
34
+ stathat.PostEZValue(name+".max", userkey, float64(h.Max()))
35
+ stathat.PostEZValue(name+".mean", userkey, float64(h.Mean()))
36
+ stathat.PostEZValue(name+".std-dev", userkey, float64(h.StdDev()))
37
+ stathat.PostEZValue(name+".50-percentile", userkey, float64(ps[0]))
38
+ stathat.PostEZValue(name+".75-percentile", userkey, float64(ps[1]))
39
+ stathat.PostEZValue(name+".95-percentile", userkey, float64(ps[2]))
40
+ stathat.PostEZValue(name+".99-percentile", userkey, float64(ps[3]))
41
+ stathat.PostEZValue(name+".999-percentile", userkey, float64(ps[4]))
42
+ case metrics.Meter:
43
+ m := metric.Snapshot()
44
+ stathat.PostEZCount(name+".count", userkey, int(m.Count()))
45
+ stathat.PostEZValue(name+".one-minute", userkey, float64(m.Rate1()))
46
+ stathat.PostEZValue(name+".five-minute", userkey, float64(m.Rate5()))
47
+ stathat.PostEZValue(name+".fifteen-minute", userkey, float64(m.Rate15()))
48
+ stathat.PostEZValue(name+".mean", userkey, float64(m.RateMean()))
49
+ case metrics.Timer:
50
+ t := metric.Snapshot()
51
+ ps := t.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999})
52
+ stathat.PostEZCount(name+".count", userkey, int(t.Count()))
53
+ stathat.PostEZValue(name+".min", userkey, float64(t.Min()))
54
+ stathat.PostEZValue(name+".max", userkey, float64(t.Max()))
55
+ stathat.PostEZValue(name+".mean", userkey, float64(t.Mean()))
56
+ stathat.PostEZValue(name+".std-dev", userkey, float64(t.StdDev()))
57
+ stathat.PostEZValue(name+".50-percentile", userkey, float64(ps[0]))
58
+ stathat.PostEZValue(name+".75-percentile", userkey, float64(ps[1]))
59
+ stathat.PostEZValue(name+".95-percentile", userkey, float64(ps[2]))
60
+ stathat.PostEZValue(name+".99-percentile", userkey, float64(ps[3]))
61
+ stathat.PostEZValue(name+".999-percentile", userkey, float64(ps[4]))
62
+ stathat.PostEZValue(name+".one-minute", userkey, float64(t.Rate1()))
63
+ stathat.PostEZValue(name+".five-minute", userkey, float64(t.Rate5()))
64
+ stathat.PostEZValue(name+".fifteen-minute", userkey, float64(t.Rate15()))
65
+ stathat.PostEZValue(name+".mean-rate", userkey, float64(t.RateMean()))
66
+ }
67
+ })
68
+ return nil
69
+}
Godeps/_workspace/src/github.com/whyrusleeping/go-metrics/syslog.go
new
+78
@@ -0,0 +1,78 @@
1
+// +build !windows
2
+
3
+package metrics
4
+
5
+import (
6
+ "fmt"
7
+ "log/syslog"
8
+ "time"
9
+)
10
+
11
+// Output each metric in the given registry to syslog periodically using
12
+// the given syslogger.
13
+func Syslog(r Registry, d time.Duration, w *syslog.Writer) {
14
+ for _ = range time.Tick(d) {
15
+ r.Each(func(name string, i interface{}) {
16
+ switch metric := i.(type) {
17
+ case Counter:
18
+ w.Info(fmt.Sprintf("counter %s: count: %d", name, metric.Count()))
19
+ case Gauge:
20
+ w.Info(fmt.Sprintf("gauge %s: value: %d", name, metric.Value()))
21
+ case GaugeFloat64:
22
+ w.Info(fmt.Sprintf("gauge %s: value: %f", name, metric.Value()))
23
+ case Healthcheck:
24
+ metric.Check()
25
+ w.Info(fmt.Sprintf("healthcheck %s: error: %v", name, metric.Error()))
26
+ case Histogram:
27
+ h := metric.Snapshot()
28
+ ps := h.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999})
29
+ w.Info(fmt.Sprintf(
30
+ "histogram %s: count: %d min: %d max: %d mean: %.2f stddev: %.2f median: %.2f 75%%: %.2f 95%%: %.2f 99%%: %.2f 99.9%%: %.2f",
31
+ name,
32
+ h.Count(),
33
+ h.Min(),
34
+ h.Max(),
35
+ h.Mean(),
36
+ h.StdDev(),
37
+ ps[0],
38
+ ps[1],
39
+ ps[2],
40
+ ps[3],
41
+ ps[4],
42
+ ))
43
+ case Meter:
44
+ m := metric.Snapshot()
45
+ w.Info(fmt.Sprintf(
46
+ "meter %s: count: %d 1-min: %.2f 5-min: %.2f 15-min: %.2f mean: %.2f",
47
+ name,
48
+ m.Count(),
49
+ m.Rate1(),
50
+ m.Rate5(),
51
+ m.Rate15(),
52
+ m.RateMean(),
53
+ ))
54
+ case Timer:
55
+ t := metric.Snapshot()
56
+ ps := t.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999})
57
+ w.Info(fmt.Sprintf(
58
+ "timer %s: count: %d min: %d max: %d mean: %.2f stddev: %.2f median: %.2f 75%%: %.2f 95%%: %.2f 99%%: %.2f 99.9%%: %.2f 1-min: %.2f 5-min: %.2f 15-min: %.2f mean-rate: %.2f",
59
+ name,
60
+ t.Count(),
61
+ t.Min(),
62
+ t.Max(),
63
+ t.Mean(),
64
+ t.StdDev(),
65
+ ps[0],
66
+ ps[1],
67
+ ps[2],
68
+ ps[3],
69
+ ps[4],
70
+ t.Rate1(),
71
+ t.Rate5(),
72
+ t.Rate15(),
73
+ t.RateMean(),
74
+ ))
75
+ }
76
+ })
77
+ }
78
+}
Godeps/_workspace/src/github.com/whyrusleeping/go-metrics/timer.go
new
+311
@@ -0,0 +1,311 @@
1
+package metrics
2
+
3
+import (
4
+ "sync"
5
+ "time"
6
+)
7
+
8
+// Timers capture the duration and rate of events.
9
+type Timer interface {
10
+ Count() int64
11
+ Max() int64
12
+ Mean() float64
13
+ Min() int64
14
+ Percentile(float64) float64
15
+ Percentiles([]float64) []float64
16
+ Rate1() float64
17
+ Rate5() float64
18
+ Rate15() float64
19
+ RateMean() float64
20
+ Snapshot() Timer
21
+ StdDev() float64
22
+ Sum() int64
23
+ Time(func())
24
+ Update(time.Duration)
25
+ UpdateSince(time.Time)
26
+ Variance() float64
27
+}
28
+
29
+// GetOrRegisterTimer returns an existing Timer or constructs and registers a
30
+// new StandardTimer.
31
+func GetOrRegisterTimer(name string, r Registry) Timer {
32
+ if nil == r {
33
+ r = DefaultRegistry
34
+ }
35
+ return r.GetOrRegister(name, NewTimer).(Timer)
36
+}
37
+
38
+// NewCustomTimer constructs a new StandardTimer from a Histogram and a Meter.
39
+func NewCustomTimer(h Histogram, m Meter) Timer {
40
+ if UseNilMetrics {
41
+ return NilTimer{}
42
+ }
43
+ return &StandardTimer{
44
+ histogram: h,
45
+ meter: m,
46
+ }
47
+}
48
+
49
+// NewRegisteredTimer constructs and registers a new StandardTimer.
50
+func NewRegisteredTimer(name string, r Registry) Timer {
51
+ c := NewTimer()
52
+ if nil == r {
53
+ r = DefaultRegistry
54
+ }
55
+ r.Register(name, c)
56
+ return c
57
+}
58
+
59
+// NewTimer constructs a new StandardTimer using an exponentially-decaying
60
+// sample with the same reservoir size and alpha as UNIX load averages.
61
+func NewTimer() Timer {
62
+ if UseNilMetrics {
63
+ return NilTimer{}
64
+ }
65
+ return &StandardTimer{
66
+ histogram: NewHistogram(NewExpDecaySample(1028, 0.015)),
67
+ meter: NewMeter(),
68
+ }
69
+}
70
+
71
+// NilTimer is a no-op Timer.
72
+type NilTimer struct {
73
+ h Histogram
74
+ m Meter
75
+}
76
+
77
+// Count is a no-op.
78
+func (NilTimer) Count() int64 { return 0 }
79
+
80
+// Max is a no-op.
81
+func (NilTimer) Max() int64 { return 0 }
82
+
83
+// Mean is a no-op.
84
+func (NilTimer) Mean() float64 { return 0.0 }
85
+
86
+// Min is a no-op.
87
+func (NilTimer) Min() int64 { return 0 }
88
+
89
+// Percentile is a no-op.
90
+func (NilTimer) Percentile(p float64) float64 { return 0.0 }
91
+
92
+// Percentiles is a no-op.
93
+func (NilTimer) Percentiles(ps []float64) []float64 {
94
+ return make([]float64, len(ps))
95
+}
96
+
97
+// Rate1 is a no-op.
98
+func (NilTimer) Rate1() float64 { return 0.0 }
99
+
100
+// Rate5 is a no-op.
101
+func (NilTimer) Rate5() float64 { return 0.0 }
102
+
103
+// Rate15 is a no-op.
104
+func (NilTimer) Rate15() float64 { return 0.0 }
105
+
106
+// RateMean is a no-op.
107
+func (NilTimer) RateMean() float64 { return 0.0 }
108
+
109
+// Snapshot is a no-op.
110
+func (NilTimer) Snapshot() Timer { return NilTimer{} }
111
+
112
+// StdDev is a no-op.
113
+func (NilTimer) StdDev() float64 { return 0.0 }
114
+
115
+// Sum is a no-op.
116
+func (NilTimer) Sum() int64 { return 0 }
117
+
118
+// Time is a no-op.
119
+func (NilTimer) Time(func()) {}
120
+
121
+// Update is a no-op.
122
+func (NilTimer) Update(time.Duration) {}
123
+
124
+// UpdateSince is a no-op.
125
+func (NilTimer) UpdateSince(time.Time) {}
126
+
127
+// Variance is a no-op.
128
+func (NilTimer) Variance() float64 { return 0.0 }
129
+
130
+// StandardTimer is the standard implementation of a Timer and uses a Histogram
131
+// and Meter.
132
+type StandardTimer struct {
133
+ histogram Histogram
134
+ meter Meter
135
+ mutex sync.Mutex
136
+}
137
+
138
+// Count returns the number of events recorded.
139
+func (t *StandardTimer) Count() int64 {
140
+ return t.histogram.Count()
141
+}
142
+
143
+// Max returns the maximum value in the sample.
144
+func (t *StandardTimer) Max() int64 {
145
+ return t.histogram.Max()
146
+}
147
+
148
+// Mean returns the mean of the values in the sample.
149
+func (t *StandardTimer) Mean() float64 {
150
+ return t.histogram.Mean()
151
+}
152
+
153
+// Min returns the minimum value in the sample.
154
+func (t *StandardTimer) Min() int64 {
155
+ return t.histogram.Min()
156
+}
157
+
158
+// Percentile returns an arbitrary percentile of the values in the sample.
159
+func (t *StandardTimer) Percentile(p float64) float64 {
160
+ return t.histogram.Percentile(p)
161
+}
162
+
163
+// Percentiles returns a slice of arbitrary percentiles of the values in the
164
+// sample.
165
+func (t *StandardTimer) Percentiles(ps []float64) []float64 {
166
+ return t.histogram.Percentiles(ps)
167
+}
168
+
169
+// Rate1 returns the one-minute moving average rate of events per second.
170
+func (t *StandardTimer) Rate1() float64 {
171
+ return t.meter.Rate1()
172
+}
173
+
174
+// Rate5 returns the five-minute moving average rate of events per second.
175
+func (t *StandardTimer) Rate5() float64 {
176
+ return t.meter.Rate5()
177
+}
178
+
179
+// Rate15 returns the fifteen-minute moving average rate of events per second.
180
+func (t *StandardTimer) Rate15() float64 {
181
+ return t.meter.Rate15()
182
+}
183
+
184
+// RateMean returns the meter's mean rate of events per second.
185
+func (t *StandardTimer) RateMean() float64 {
186
+ return t.meter.RateMean()
187
+}
188
+
189
+// Snapshot returns a read-only copy of the timer.
190
+func (t *StandardTimer) Snapshot() Timer {
191
+ t.mutex.Lock()
192
+ defer t.mutex.Unlock()
193
+ return &TimerSnapshot{
194
+ histogram: t.histogram.Snapshot().(*HistogramSnapshot),
195
+ meter: t.meter.Snapshot().(*MeterSnapshot),
196
+ }
197
+}
198
+
199
+// StdDev returns the standard deviation of the values in the sample.
200
+func (t *StandardTimer) StdDev() float64 {
201
+ return t.histogram.StdDev()
202
+}
203
+
204
+// Sum returns the sum in the sample.
205
+func (t *StandardTimer) Sum() int64 {
206
+ return t.histogram.Sum()
207
+}
208
+
209
+// Record the duration of the execution of the given function.
210
+func (t *StandardTimer) Time(f func()) {
211
+ ts := time.Now()
212
+ f()
213
+ t.Update(time.Since(ts))
214
+}
215
+
216
+// Record the duration of an event.
217
+func (t *StandardTimer) Update(d time.Duration) {
218
+ t.mutex.Lock()
219
+ defer t.mutex.Unlock()
220
+ t.histogram.Update(int64(d))
221
+ t.meter.Mark(1)
222
+}
223
+
224
+// Record the duration of an event that started at a time and ends now.
225
+func (t *StandardTimer) UpdateSince(ts time.Time) {
226
+ t.mutex.Lock()
227
+ defer t.mutex.Unlock()
228
+ t.histogram.Update(int64(time.Since(ts)))
229
+ t.meter.Mark(1)
230
+}
231
+
232
+// Variance returns the variance of the values in the sample.
233
+func (t *StandardTimer) Variance() float64 {
234
+ return t.histogram.Variance()
235
+}
236
+
237
+// TimerSnapshot is a read-only copy of another Timer.
238
+type TimerSnapshot struct {
239
+ histogram *HistogramSnapshot
240
+ meter *MeterSnapshot
241
+}
242
+
243
+// Count returns the number of events recorded at the time the snapshot was
244
+// taken.
245
+func (t *TimerSnapshot) Count() int64 { return t.histogram.Count() }
246
+
247
+// Max returns the maximum value at the time the snapshot was taken.
248
+func (t *TimerSnapshot) Max() int64 { return t.histogram.Max() }
249
+
250
+// Mean returns the mean value at the time the snapshot was taken.
251
+func (t *TimerSnapshot) Mean() float64 { return t.histogram.Mean() }
252
+
253
+// Min returns the minimum value at the time the snapshot was taken.
254
+func (t *TimerSnapshot) Min() int64 { return t.histogram.Min() }
255
+
256
+// Percentile returns an arbitrary percentile of sampled values at the time the
257
+// snapshot was taken.
258
+func (t *TimerSnapshot) Percentile(p float64) float64 {
259
+ return t.histogram.Percentile(p)
260
+}
261
+
262
+// Percentiles returns a slice of arbitrary percentiles of sampled values at
263
+// the time the snapshot was taken.
264
+func (t *TimerSnapshot) Percentiles(ps []float64) []float64 {
265
+ return t.histogram.Percentiles(ps)
266
+}
267
+
268
+// Rate1 returns the one-minute moving average rate of events per second at the
269
+// time the snapshot was taken.
270
+func (t *TimerSnapshot) Rate1() float64 { return t.meter.Rate1() }
271
+
272
+// Rate5 returns the five-minute moving average rate of events per second at
273
+// the time the snapshot was taken.
274
+func (t *TimerSnapshot) Rate5() float64 { return t.meter.Rate5() }
275
+
276
+// Rate15 returns the fifteen-minute moving average rate of events per second
277
+// at the time the snapshot was taken.
278
+func (t *TimerSnapshot) Rate15() float64 { return t.meter.Rate15() }
279
+
280
+// RateMean returns the meter's mean rate of events per second at the time the
281
+// snapshot was taken.
282
+func (t *TimerSnapshot) RateMean() float64 { return t.meter.RateMean() }
283
+
284
+// Snapshot returns the snapshot.
285
+func (t *TimerSnapshot) Snapshot() Timer { return t }
286
+
287
+// StdDev returns the standard deviation of the values at the time the snapshot
288
+// was taken.
289
+func (t *TimerSnapshot) StdDev() float64 { return t.histogram.StdDev() }
290
+
291
+// Sum returns the sum at the time the snapshot was taken.
292
+func (t *TimerSnapshot) Sum() int64 { return t.histogram.Sum() }
293
+
294
+// Time panics.
295
+func (*TimerSnapshot) Time(func()) {
296
+ panic("Time called on a TimerSnapshot")
297
+}
298
+
299
+// Update panics.
300
+func (*TimerSnapshot) Update(time.Duration) {
301
+ panic("Update called on a TimerSnapshot")
302
+}
303
+
304
+// UpdateSince panics.
305
+func (*TimerSnapshot) UpdateSince(time.Time) {
306
+ panic("UpdateSince called on a TimerSnapshot")
307
+}
308
+
309
+// Variance returns the variance of the values at the time the snapshot was
310
+// taken.
311
+func (t *TimerSnapshot) Variance() float64 { return t.histogram.Variance() }
Godeps/_workspace/src/github.com/whyrusleeping/go-metrics/timer_test.go
new
+81
@@ -0,0 +1,81 @@
1
+package metrics
2
+
3
+import (
4
+ "math"
5
+ "testing"
6
+ "time"
7
+)
8
+
9
+func BenchmarkTimer(b *testing.B) {
10
+ tm := NewTimer()
11
+ b.ResetTimer()
12
+ for i := 0; i < b.N; i++ {
13
+ tm.Update(1)
14
+ }
15
+}
16
+
17
+func TestGetOrRegisterTimer(t *testing.T) {
18
+ r := NewRegistry()
19
+ NewRegisteredTimer("foo", r).Update(47)
20
+ if tm := GetOrRegisterTimer("foo", r); 1 != tm.Count() {
21
+ t.Fatal(tm)
22
+ }
23
+}
24
+
25
+func TestTimerExtremes(t *testing.T) {
26
+ tm := NewTimer()
27
+ tm.Update(math.MaxInt64)
28
+ tm.Update(0)
29
+ if stdDev := tm.StdDev(); 4.611686018427388e+18 != stdDev {
30
+ t.Errorf("tm.StdDev(): 4.611686018427388e+18 != %v\n", stdDev)
31
+ }
32
+}
33
+
34
+func TestTimerFunc(t *testing.T) {
35
+ tm := NewTimer()
36
+ tm.Time(func() { time.Sleep(50e6) })
37
+ if max := tm.Max(); 45e6 > max || max > 55e6 {
38
+ t.Errorf("tm.Max(): 45e6 > %v || %v > 55e6\n", max, max)
39
+ }
40
+}
41
+
42
+func TestTimerZero(t *testing.T) {
43
+ tm := NewTimer()
44
+ if count := tm.Count(); 0 != count {
45
+ t.Errorf("tm.Count(): 0 != %v\n", count)
46
+ }
47
+ if min := tm.Min(); 0 != min {
48
+ t.Errorf("tm.Min(): 0 != %v\n", min)
49
+ }
50
+ if max := tm.Max(); 0 != max {
51
+ t.Errorf("tm.Max(): 0 != %v\n", max)
52
+ }
53
+ if mean := tm.Mean(); 0.0 != mean {
54
+ t.Errorf("tm.Mean(): 0.0 != %v\n", mean)
55
+ }
56
+ if stdDev := tm.StdDev(); 0.0 != stdDev {
57
+ t.Errorf("tm.StdDev(): 0.0 != %v\n", stdDev)
58
+ }
59
+ ps := tm.Percentiles([]float64{0.5, 0.75, 0.99})
60
+ if 0.0 != ps[0] {
61
+ t.Errorf("median: 0.0 != %v\n", ps[0])
62
+ }
63
+ if 0.0 != ps[1] {
64
+ t.Errorf("75th percentile: 0.0 != %v\n", ps[1])
65
+ }
66
+ if 0.0 != ps[2] {
67
+ t.Errorf("99th percentile: 0.0 != %v\n", ps[2])
68
+ }
69
+ if rate1 := tm.Rate1(); 0.0 != rate1 {
70
+ t.Errorf("tm.Rate1(): 0.0 != %v\n", rate1)
71
+ }
72
+ if rate5 := tm.Rate5(); 0.0 != rate5 {
73
+ t.Errorf("tm.Rate5(): 0.0 != %v\n", rate5)
74
+ }
75
+ if rate15 := tm.Rate15(); 0.0 != rate15 {
76
+ t.Errorf("tm.Rate15(): 0.0 != %v\n", rate15)
77
+ }
78
+ if rateMean := tm.RateMean(); 0.0 != rateMean {
79
+ t.Errorf("tm.RateMean(): 0.0 != %v\n", rateMean)
80
+ }
81
+}
Godeps/_workspace/src/github.com/whyrusleeping/go-metrics/writer.go
new
+100
@@ -0,0 +1,100 @@
1
+package metrics
2
+
3
+import (
4
+ "fmt"
5
+ "io"
6
+ "sort"
7
+ "time"
8
+)
9
+
10
+// Write sorts writes each metric in the given registry periodically to the
11
+// given io.Writer.
12
+func Write(r Registry, d time.Duration, w io.Writer) {
13
+ for _ = range time.Tick(d) {
14
+ WriteOnce(r, w)
15
+ }
16
+}
17
+
18
+// WriteOnce sorts and writes metrics in the given registry to the given
19
+// io.Writer.
20
+func WriteOnce(r Registry, w io.Writer) {
21
+ var namedMetrics namedMetricSlice
22
+ r.Each(func(name string, i interface{}) {
23
+ namedMetrics = append(namedMetrics, namedMetric{name, i})
24
+ })
25
+
26
+ sort.Sort(namedMetrics)
27
+ for _, namedMetric := range namedMetrics {
28
+ switch metric := namedMetric.m.(type) {
29
+ case Counter:
30
+ fmt.Fprintf(w, "counter %s\n", namedMetric.name)
31
+ fmt.Fprintf(w, " count: %9d\n", metric.Count())
32
+ case Gauge:
33
+ fmt.Fprintf(w, "gauge %s\n", namedMetric.name)
34
+ fmt.Fprintf(w, " value: %9d\n", metric.Value())
35
+ case GaugeFloat64:
36
+ fmt.Fprintf(w, "gauge %s\n", namedMetric.name)
37
+ fmt.Fprintf(w, " value: %f\n", metric.Value())
38
+ case Healthcheck:
39
+ metric.Check()
40
+ fmt.Fprintf(w, "healthcheck %s\n", namedMetric.name)
41
+ fmt.Fprintf(w, " error: %v\n", metric.Error())
42
+ case Histogram:
43
+ h := metric.Snapshot()
44
+ ps := h.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999})
45
+ fmt.Fprintf(w, "histogram %s\n", namedMetric.name)
46
+ fmt.Fprintf(w, " count: %9d\n", h.Count())
47
+ fmt.Fprintf(w, " min: %9d\n", h.Min())
48
+ fmt.Fprintf(w, " max: %9d\n", h.Max())
49
+ fmt.Fprintf(w, " mean: %12.2f\n", h.Mean())
50
+ fmt.Fprintf(w, " stddev: %12.2f\n", h.StdDev())
51
+ fmt.Fprintf(w, " median: %12.2f\n", ps[0])
52
+ fmt.Fprintf(w, " 75%%: %12.2f\n", ps[1])
53
+ fmt.Fprintf(w, " 95%%: %12.2f\n", ps[2])
54
+ fmt.Fprintf(w, " 99%%: %12.2f\n", ps[3])
55
+ fmt.Fprintf(w, " 99.9%%: %12.2f\n", ps[4])
56
+ case Meter:
57
+ m := metric.Snapshot()
58
+ fmt.Fprintf(w, "meter %s\n", namedMetric.name)
59
+ fmt.Fprintf(w, " count: %9d\n", m.Count())
60
+ fmt.Fprintf(w, " 1-min rate: %12.2f\n", m.Rate1())
61
+ fmt.Fprintf(w, " 5-min rate: %12.2f\n", m.Rate5())
62
+ fmt.Fprintf(w, " 15-min rate: %12.2f\n", m.Rate15())
63
+ fmt.Fprintf(w, " mean rate: %12.2f\n", m.RateMean())
64
+ case Timer:
65
+ t := metric.Snapshot()
66
+ ps := t.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999})
67
+ fmt.Fprintf(w, "timer %s\n", namedMetric.name)
68
+ fmt.Fprintf(w, " count: %9d\n", t.Count())
69
+ fmt.Fprintf(w, " min: %9d\n", t.Min())
70
+ fmt.Fprintf(w, " max: %9d\n", t.Max())
71
+ fmt.Fprintf(w, " mean: %12.2f\n", t.Mean())
72
+ fmt.Fprintf(w, " stddev: %12.2f\n", t.StdDev())
73
+ fmt.Fprintf(w, " median: %12.2f\n", ps[0])
74
+ fmt.Fprintf(w, " 75%%: %12.2f\n", ps[1])
75
+ fmt.Fprintf(w, " 95%%: %12.2f\n", ps[2])
76
+ fmt.Fprintf(w, " 99%%: %12.2f\n", ps[3])
77
+ fmt.Fprintf(w, " 99.9%%: %12.2f\n", ps[4])
78
+ fmt.Fprintf(w, " 1-min rate: %12.2f\n", t.Rate1())
79
+ fmt.Fprintf(w, " 5-min rate: %12.2f\n", t.Rate5())
80
+ fmt.Fprintf(w, " 15-min rate: %12.2f\n", t.Rate15())
81
+ fmt.Fprintf(w, " mean rate: %12.2f\n", t.RateMean())
82
+ }
83
+ }
84
+}
85
+
86
+type namedMetric struct {
87
+ name string
88
+ m interface{}
89
+}
90
+
91
+// namedMetricSlice is a slice of namedMetrics that implements sort.Interface.
92
+type namedMetricSlice []namedMetric
93
+
94
+func (nms namedMetricSlice) Len() int { return len(nms) }
95
+
96
+func (nms namedMetricSlice) Swap(i, j int) { nms[i], nms[j] = nms[j], nms[i] }
97
+
98
+func (nms namedMetricSlice) Less(i, j int) bool {
99
+ return nms[i].name < nms[j].name
100
+}
Godeps/_workspace/src/github.com/whyrusleeping/go-metrics/writer_test.go
new
+22
@@ -0,0 +1,22 @@
1
+package metrics
2
+
3
+import (
4
+ "sort"
5
+ "testing"
6
+)
7
+
8
+func TestMetricsSorting(t *testing.T) {
9
+ var namedMetrics = namedMetricSlice{
10
+ {name: "zzz"},
11
+ {name: "bbb"},
12
+ {name: "fff"},
13
+ {name: "ggg"},
14
+ }
15
+
16
+ sort.Sort(namedMetrics)
17
+ for i, name := range []string{"bbb", "fff", "ggg", "zzz"} {
18
+ if namedMetrics[i].name != name {
19
+ t.Fail()
20
+ }
21
+ }
22
+}
core/commands/root.go
+1
@@ -95,6 +95,7 @@ var rootSubcommands = map[string]*cmds.Command{
95
"ping": PingCmd,
96
"refs": RefsCmd,
97
"repo": RepoCmd,
98
+ "stats": StatsCmd,
99
"swarm": SwarmCmd,
100
"update": UpdateCmd,
101
"version": VersionCmd,
core/commands/stat.go
new
+181
@@ -0,0 +1,181 @@
1
+package commands
2
+
3
+import (
4
+ "bytes"
5
+ "errors"
6
+ "fmt"
7
+ "io"
8
+ "time"
9
+
10
+ humanize "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/dustin/go-humanize"
11
+
12
+ cmds "github.com/ipfs/go-ipfs/commands"
13
+ metrics "github.com/ipfs/go-ipfs/metrics"
14
+ peer "github.com/ipfs/go-ipfs/p2p/peer"
15
+ protocol "github.com/ipfs/go-ipfs/p2p/protocol"
16
+ u "github.com/ipfs/go-ipfs/util"
17
+)
18
+
19
+var StatsCmd = &cmds.Command{
20
+ Helptext: cmds.HelpText{
21
+ Tagline: "Query IPFS statistics",
22
+ ShortDescription: ``,
23
+ },
24
+
25
+ Subcommands: map[string]*cmds.Command{
26
+ "bw": statBwCmd,
27
+ },
28
+}
29
+
30
+var statBwCmd = &cmds.Command{
31
+ Helptext: cmds.HelpText{
32
+ Tagline: "Print ipfs bandwidth information",
33
+ ShortDescription: ``,
34
+ },
35
+ Options: []cmds.Option{
36
+ cmds.StringOption("peer", "p", "specify a peer to print bandwidth for"),
37
+ cmds.StringOption("proto", "t", "specify a protocol to print bandwidth for"),
38
+ cmds.BoolOption("poll", "specify a protocol to print bandwidth for"),
39
+ cmds.StringOption("interval", "i", "time interval to wait between updating output"),
40
+ },
41
+
42
+ Run: func(req cmds.Request, res cmds.Response) {
43
+ nd, err := req.Context().GetNode()
44
+ if err != nil {
45
+ res.SetError(err, cmds.ErrNormal)
46
+ return
47
+ }
48
+
49
+ // Must be online!
50
+ if !nd.OnlineMode() {
51
+ res.SetError(errNotOnline, cmds.ErrClient)
52
+ return
53
+ }
54
+
55
+ pstr, pfound, err := req.Option("peer").String()
56
+ if err != nil {
57
+ res.SetError(err, cmds.ErrNormal)
58
+ return
59
+ }
60
+
61
+ tstr, tfound, err := req.Option("proto").String()
62
+ if err != nil {
63
+ res.SetError(err, cmds.ErrNormal)
64
+ return
65
+ }
66
+ if pfound && tfound {
67
+ res.SetError(errors.New("please only specify peer OR protocol"), cmds.ErrClient)
68
+ return
69
+ }
70
+
71
+ var pid peer.ID
72
+ if pfound {
73
+ checkpid, err := peer.IDB58Decode(pstr)
74
+ if err != nil {
75
+ res.SetError(err, cmds.ErrNormal)
76
+ return
77
+ }
78
+ pid = checkpid
79
+ }
80
+
81
+ interval := time.Second
82
+ timeS, found, err := req.Option("interval").String()
83
+ if err != nil {
84
+ res.SetError(err, cmds.ErrNormal)
85
+ return
86
+ }
87
+ if found {
88
+ v, err := time.ParseDuration(timeS)
89
+ if err != nil {
90
+ res.SetError(err, cmds.ErrNormal)
91
+ return
92
+ }
93
+ interval = v
94
+ }
95
+
96
+ doPoll, _, err := req.Option("poll").Bool()
97
+ if err != nil {
98
+ res.SetError(err, cmds.ErrNormal)
99
+ return
100
+ }
101
+
102
+ out := make(chan interface{})
103
+ res.SetOutput((<-chan interface{})(out))
104
+
105
+ go func() {
106
+ defer close(out)
107
+ for {
108
+ if pfound {
109
+ stats := nd.Reporter.GetBandwidthForPeer(pid)
110
+ out <- &stats
111
+ } else if tfound {
112
+ protoId := protocol.ID(tstr)
113
+ stats := nd.Reporter.GetBandwidthForProtocol(protoId)
114
+ out <- &stats
115
+ } else {
116
+ totals := nd.Reporter.GetBandwidthTotals()
117
+ out <- &totals
118
+ }
119
+ if !doPoll {
120
+ return
121
+ }
122
+ select {
123
+ case <-time.After(interval):
124
+ case <-req.Context().Context.Done():
125
+ return
126
+ }
127
+ }
128
+ }()
129
+ },
130
+ Type: metrics.Stats{},
131
+ Marshalers: cmds.MarshalerMap{
132
+ cmds.Text: func(res cmds.Response) (io.Reader, error) {
133
+ outCh, ok := res.Output().(<-chan interface{})
134
+ if !ok {
135
+ return nil, u.ErrCast()
136
+ }
137
+
138
+ polling, _, err := res.Request().Option("poll").Bool()
139
+ if err != nil {
140
+ return nil, err
141
+ }
142
+
143
+ first := true
144
+ marshal := func(v interface{}) (io.Reader, error) {
145
+ bs, ok := v.(*metrics.Stats)
146
+ if !ok {
147
+ return nil, u.ErrCast()
148
+ }
149
+ out := new(bytes.Buffer)
150
+ if !polling {
151
+ printStats(out, bs)
152
+ } else {
153
+ if first {
154
+ fmt.Fprintln(out, "Total Up\t Total Down\t Rate Up\t Rate Down")
155
+ first = false
156
+ }
157
+ fmt.Fprint(out, "\r")
158
+ fmt.Fprintf(out, "%s \t\t", humanize.Bytes(uint64(bs.TotalOut)))
159
+ fmt.Fprintf(out, " %s \t\t", humanize.Bytes(uint64(bs.TotalIn)))
160
+ fmt.Fprintf(out, " %s/s \t", humanize.Bytes(uint64(bs.RateOut)))
161
+ fmt.Fprintf(out, " %s/s ", humanize.Bytes(uint64(bs.RateIn)))
162
+ }
163
+ return out, nil
164
+
165
+ }
166
+
167
+ return &cmds.ChannelMarshaler{
168
+ Channel: outCh,
169
+ Marshaler: marshal,
170
+ }, nil
171
+ },
172
+ },
173
+}
174
+
175
+func printStats(out io.Writer, bs *metrics.Stats) {
176
+ fmt.Fprintln(out, "Bandwidth")
177
+ fmt.Fprintf(out, "TotalIn: %s\n", humanize.Bytes(uint64(bs.TotalIn)))
178
+ fmt.Fprintf(out, "TotalOut: %s\n", humanize.Bytes(uint64(bs.TotalOut)))
179
+ fmt.Fprintf(out, "RateIn: %s/s\n", humanize.Bytes(uint64(bs.RateIn)))
180
+ fmt.Fprintf(out, "RateOut: %s/s\n", humanize.Bytes(uint64(bs.RateOut)))
181
+}
core/core.go
+11
-5
@@ -12,6 +12,7 @@ import (
12
ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore"
13
ma "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr"
14
context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context"
15
+ metrics "github.com/ipfs/go-ipfs/metrics"
16
eventlog "github.com/ipfs/go-ipfs/thirdparty/eventlog"
17
debugerror "github.com/ipfs/go-ipfs/util/debugerror"
18
@@ -81,6 +82,7 @@ type IpfsNode struct {
82
Blocks *bserv.BlockService // the block service, get/add blocks.
83
DAG merkledag.DAGService // the merkle dag service, get/add objects.
84
Resolver *path.Resolver // the path resolution system
85
+ Reporter metrics.Reporter
86
87
// Online
88
PeerHost p2phost.Host // the network host (server+client)
@@ -239,7 +241,10 @@ func (n *IpfsNode) startOnlineServices(ctx context.Context, routingOption Routin
241
return err
242
}
243
242
- peerhost, err := hostOption(ctx, n.Identity, n.Peerstore)
244
+ // Set reporter
245
+ n.Reporter = metrics.NewBandwidthCounter()
246
+
247
+ peerhost, err := hostOption(ctx, n.Identity, n.Peerstore, n.Reporter)
248
if err != nil {
249
return debugerror.Wrap(err)
250
}
@@ -465,20 +470,21 @@ func listenAddresses(cfg *config.Config) ([]ma.Multiaddr, error) {
470
return listen, nil
471
}
472
468
-type HostOption func(ctx context.Context, id peer.ID, ps peer.Peerstore) (p2phost.Host, error)
473
+type HostOption func(ctx context.Context, id peer.ID, ps peer.Peerstore, bwr metrics.Reporter) (p2phost.Host, error)
474
475
var DefaultHostOption HostOption = constructPeerHost
476
477
// isolates the complex initialization steps
473
-func constructPeerHost(ctx context.Context, id peer.ID, ps peer.Peerstore) (p2phost.Host, error) {
478
+func constructPeerHost(ctx context.Context, id peer.ID, ps peer.Peerstore, bwr metrics.Reporter) (p2phost.Host, error) {
479
480
// no addresses to begin with. we'll start later.
476
- network, err := swarm.NewNetwork(ctx, nil, id, ps)
481
+ network, err := swarm.NewNetwork(ctx, nil, id, ps, bwr)
482
if err != nil {
483
return nil, debugerror.Wrap(err)
484
}
485
481
- host := p2pbhost.New(network, p2pbhost.NATPortMap)
486
+ host := p2pbhost.New(network, p2pbhost.NATPortMap, bwr)
487
+
488
return host, nil
489
}
490
metrics/bw_stats.go
new
+89
@@ -0,0 +1,89 @@
1
+package metrics
2
+
3
+import (
4
+ gm "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/whyrusleeping/go-metrics"
5
+ "sync"
6
+
7
+ peer "github.com/ipfs/go-ipfs/p2p/peer"
8
+ protocol "github.com/ipfs/go-ipfs/p2p/protocol"
9
+)
10
+
11
+type Stats struct {
12
+ TotalIn int64
13
+ TotalOut int64
14
+ RateIn float64
15
+ RateOut float64
16
+}
17
+
18
+type BandwidthCounter struct {
19
+ lock sync.Mutex
20
+ totalIn gm.Meter
21
+ totalOut gm.Meter
22
+ reg gm.Registry
23
+}
24
+
25
+func NewBandwidthCounter() *BandwidthCounter {
26
+ reg := gm.NewRegistry()
27
+ return &BandwidthCounter{
28
+ totalIn: gm.GetOrRegisterMeter("totalIn", reg),
29
+ totalOut: gm.GetOrRegisterMeter("totalOut", reg),
30
+ reg: reg,
31
+ }
32
+}
33
+
34
+func (bwc *BandwidthCounter) LogSentMessage(size int64) {
35
+ bwc.totalOut.Mark(size)
36
+}
37
+
38
+func (bwc *BandwidthCounter) LogRecvMessage(size int64) {
39
+ bwc.totalIn.Mark(size)
40
+}
41
+
42
+func (bwc *BandwidthCounter) LogSentMessageStream(size int64, proto protocol.ID, p peer.ID) {
43
+ meter := gm.GetOrRegisterMeter("/peer/out/"+string(p), bwc.reg)
44
+ meter.Mark(size)
45
+
46
+ pmeter := gm.GetOrRegisterMeter("/proto/out/"+string(proto), bwc.reg)
47
+ pmeter.Mark(size)
48
+}
49
+
50
+func (bwc *BandwidthCounter) LogRecvMessageStream(size int64, proto protocol.ID, p peer.ID) {
51
+ meter := gm.GetOrRegisterMeter("/peer/in/"+string(p), bwc.reg)
52
+ meter.Mark(size)
53
+
54
+ pmeter := gm.GetOrRegisterMeter("/proto/in/"+string(proto), bwc.reg)
55
+ pmeter.Mark(size)
56
+}
57
+
58
+func (bwc *BandwidthCounter) GetBandwidthForPeer(p peer.ID) (out Stats) {
59
+ inMeter := gm.GetOrRegisterMeter("/peer/in/"+string(p), bwc.reg).Snapshot()
60
+ outMeter := gm.GetOrRegisterMeter("/peer/out/"+string(p), bwc.reg).Snapshot()
61
+
62
+ return Stats{
63
+ TotalIn: inMeter.Count(),
64
+ TotalOut: outMeter.Count(),
65
+ RateIn: inMeter.RateFine(),
66
+ RateOut: outMeter.RateFine(),
67
+ }
68
+}
69
+
70
+func (bwc *BandwidthCounter) GetBandwidthForProtocol(proto protocol.ID) (out Stats) {
71
+ inMeter := gm.GetOrRegisterMeter(string("/proto/in/"+proto), bwc.reg).Snapshot()
72
+ outMeter := gm.GetOrRegisterMeter(string("/proto/out/"+proto), bwc.reg).Snapshot()
73
+
74
+ return Stats{
75
+ TotalIn: inMeter.Count(),
76
+ TotalOut: outMeter.Count(),
77
+ RateIn: inMeter.RateFine(),
78
+ RateOut: outMeter.RateFine(),
79
+ }
80
+}
81
+
82
+func (bwc *BandwidthCounter) GetBandwidthTotals() (out Stats) {
83
+ return Stats{
84
+ TotalIn: bwc.totalIn.Count(),
85
+ TotalOut: bwc.totalOut.Count(),
86
+ RateIn: bwc.totalIn.RateFine(),
87
+ RateOut: bwc.totalOut.RateFine(),
88
+ }
89
+}
metrics/conn/conn.go
new
+39
@@ -0,0 +1,39 @@
1
+package meterconn
2
+
3
+import (
4
+ manet "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr-net"
5
+ metrics "github.com/ipfs/go-ipfs/metrics"
6
+)
7
+
8
+type MeteredConn struct {
9
+ mesRecv metrics.MeterCallback
10
+ mesSent metrics.MeterCallback
11
+
12
+ manet.Conn
13
+}
14
+
15
+func WrapConn(bwc metrics.Reporter, c manet.Conn) manet.Conn {
16
+ return newMeteredConn(c, bwc.LogRecvMessage, bwc.LogSentMessage)
17
+}
18
+
19
+func newMeteredConn(base manet.Conn, rcb metrics.MeterCallback, scb metrics.MeterCallback) manet.Conn {
20
+ return &MeteredConn{
21
+ Conn: base,
22
+ mesRecv: rcb,
23
+ mesSent: scb,
24
+ }
25
+}
26
+
27
+func (mc *MeteredConn) Read(b []byte) (int, error) {
28
+ n, err := mc.Conn.Read(b)
29
+
30
+ mc.mesRecv(int64(n))
31
+ return n, err
32
+}
33
+
34
+func (mc *MeteredConn) Write(b []byte) (int, error) {
35
+ n, err := mc.Conn.Write(b)
36
+
37
+ mc.mesSent(int64(n))
38
+ return n, err
39
+}
metrics/interface.go
new
+19
@@ -0,0 +1,19 @@
1
+package metrics
2
+
3
+import (
4
+ peer "github.com/ipfs/go-ipfs/p2p/peer"
5
+ protocol "github.com/ipfs/go-ipfs/p2p/protocol"
6
+)
7
+
8
+type StreamMeterCallback func(int64, protocol.ID, peer.ID)
9
+type MeterCallback func(int64)
10
+
11
+type Reporter interface {
12
+ LogSentMessage(int64)
13
+ LogRecvMessage(int64)
14
+ LogSentMessageStream(int64, protocol.ID, peer.ID)
15
+ LogRecvMessageStream(int64, protocol.ID, peer.ID)
16
+ GetBandwidthForPeer(peer.ID) Stats
17
+ GetBandwidthForProtocol(protocol.ID) Stats
18
+ GetBandwidthTotals() Stats
19
+}
metrics/stream/metered.go
new
+52
@@ -0,0 +1,52 @@
1
+package meterstream
2
+
3
+import (
4
+ metrics "github.com/ipfs/go-ipfs/metrics"
5
+ inet "github.com/ipfs/go-ipfs/p2p/net"
6
+ peer "github.com/ipfs/go-ipfs/p2p/peer"
7
+ protocol "github.com/ipfs/go-ipfs/p2p/protocol"
8
+)
9
+
10
+type meteredStream struct {
11
+ // keys for accessing metrics data
12
+ protoKey protocol.ID
13
+ peerKey peer.ID
14
+
15
+ inet.Stream
16
+
17
+ // callbacks for reporting bandwidth usage
18
+ mesSent metrics.StreamMeterCallback
19
+ mesRecv metrics.StreamMeterCallback
20
+}
21
+
22
+func newMeteredStream(base inet.Stream, pid protocol.ID, p peer.ID, recvCB, sentCB metrics.StreamMeterCallback) inet.Stream {
23
+ return &meteredStream{
24
+ Stream: base,
25
+ mesSent: sentCB,
26
+ mesRecv: recvCB,
27
+ protoKey: pid,
28
+ peerKey: p,
29
+ }
30
+}
31
+
32
+func WrapStream(base inet.Stream, pid protocol.ID, bwc metrics.Reporter) inet.Stream {
33
+ return newMeteredStream(base, pid, base.Conn().RemotePeer(), bwc.LogRecvMessageStream, bwc.LogSentMessageStream)
34
+}
35
+
36
+func (s *meteredStream) Read(b []byte) (int, error) {
37
+ n, err := s.Stream.Read(b)
38
+
39
+ // Log bytes read
40
+ s.mesRecv(int64(n), s.protoKey, s.peerKey)
41
+
42
+ return n, err
43
+}
44
+
45
+func (s *meteredStream) Write(b []byte) (int, error) {
46
+ n, err := s.Stream.Write(b)
47
+
48
+ // Log bytes written
49
+ s.mesSent(int64(n), s.protoKey, s.peerKey)
50
+
51
+ return n, err
52
+}
metrics/stream/metered_test.go
new
+74
@@ -0,0 +1,74 @@
1
+package meterstream
2
+
3
+import (
4
+ "io"
5
+ "io/ioutil"
6
+ "testing"
7
+
8
+ inet "github.com/ipfs/go-ipfs/p2p/net"
9
+ peer "github.com/ipfs/go-ipfs/p2p/peer"
10
+ protocol "github.com/ipfs/go-ipfs/p2p/protocol"
11
+ u "github.com/ipfs/go-ipfs/util"
12
+)
13
+
14
+type FakeStream struct {
15
+ ReadBuf io.Reader
16
+ inet.Stream
17
+}
18
+
19
+func (fs *FakeStream) Read(b []byte) (int, error) {
20
+ return fs.ReadBuf.Read(b)
21
+}
22
+
23
+func (fs *FakeStream) Write(b []byte) (int, error) {
24
+ return len(b), nil
25
+}
26
+
27
+func TestCallbacksWork(t *testing.T) {
28
+ fake := new(FakeStream)
29
+
30
+ var sent int64
31
+ var recv int64
32
+
33
+ sentCB := func(n int64, proto protocol.ID, p peer.ID) {
34
+ sent += n
35
+ }
36
+
37
+ recvCB := func(n int64, proto protocol.ID, p peer.ID) {
38
+ recv += n
39
+ }
40
+
41
+ ms := newMeteredStream(fake, protocol.ID("TEST"), peer.ID("PEER"), recvCB, sentCB)
42
+
43
+ toWrite := int64(100000)
44
+ toRead := int64(100000)
45
+
46
+ fake.ReadBuf = io.LimitReader(u.NewTimeSeededRand(), toRead)
47
+ writeData := io.LimitReader(u.NewTimeSeededRand(), toWrite)
48
+
49
+ n, err := io.Copy(ms, writeData)
50
+ if err != nil {
51
+ t.Fatal(err)
52
+ }
53
+
54
+ if n != toWrite {
55
+ t.Fatal("incorrect write amount")
56
+ }
57
+
58
+ if toWrite != sent {
59
+ t.Fatal("incorrectly reported writes", toWrite, sent)
60
+ }
61
+
62
+ n, err = io.Copy(ioutil.Discard, ms)
63
+ if err != nil {
64
+ t.Fatal(err)
65
+ }
66
+
67
+ if n != toRead {
68
+ t.Fatal("incorrect read amount")
69
+ }
70
+
71
+ if toRead != recv {
72
+ t.Fatal("incorrectly reported reads")
73
+ }
74
+}
p2p/host/basic/basic_host.go
+36
-11
@@ -4,6 +4,8 @@ import (
4
ma "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr"
5
goprocess "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess"
6
context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context"
7
+ metrics "github.com/ipfs/go-ipfs/metrics"
8
+ mstream "github.com/ipfs/go-ipfs/metrics/stream"
9
eventlog "github.com/ipfs/go-ipfs/thirdparty/eventlog"
10
11
inet "github.com/ipfs/go-ipfs/p2p/net"
@@ -41,13 +43,16 @@ type BasicHost struct {
43
natmgr *natManager
44
45
proc goprocess.Process
46
+
47
+ bwc metrics.Reporter
48
}
49
50
// New constructs and sets up a new *BasicHost with given Network
47
-func New(net inet.Network, opts ...Option) *BasicHost {
51
+func New(net inet.Network, opts ...interface{}) *BasicHost {
52
h := &BasicHost{
53
network: net,
54
mux: protocol.NewMux(),
55
+ bwc: metrics.NewBandwidthCounter(),
56
}
57
58
h.proc = goprocess.WithTeardown(func() error {
@@ -62,16 +67,21 @@ func New(net inet.Network, opts ...Option) *BasicHost {
67
h.ids = identify.NewIDService(h)
68
h.relay = relay.NewRelayService(h, h.Mux().HandleSync)
69
65
- net.SetConnHandler(h.newConnHandler)
66
- net.SetStreamHandler(h.newStreamHandler)
67
-
70
for _, o := range opts {
69
- switch o {
70
- case NATPortMap:
71
- h.natmgr = newNatManager(h)
71
+ switch o := o.(type) {
72
+ case Option:
73
+ switch o {
74
+ case NATPortMap:
75
+ h.natmgr = newNatManager(h)
76
+ }
77
+ case metrics.Reporter:
78
+ h.bwc = o
79
}
80
}
81
82
+ net.SetConnHandler(h.newConnHandler)
83
+ net.SetStreamHandler(h.newStreamHandler)
84
+
85
return h
86
}
87
@@ -81,8 +91,17 @@ func (h *BasicHost) newConnHandler(c inet.Conn) {
91
}
92
93
// newStreamHandler is the remote-opened stream handler for inet.Network
94
+// TODO: this feels a bit wonky
95
func (h *BasicHost) newStreamHandler(s inet.Stream) {
85
- h.Mux().Handle(s)
96
+ protoID, handle, err := h.Mux().ReadHeader(s)
97
+ if err != nil {
98
+ log.Error("protocol mux failed: %s", err)
99
+ return
100
+ }
101
+
102
+ logStream := mstream.WrapStream(s, protoID, h.bwc)
103
+
104
+ go handle(logStream)
105
}
106
107
// ID returns the (local) peer.ID associated with this Host
@@ -131,12 +150,14 @@ func (h *BasicHost) NewStream(pid protocol.ID, p peer.ID) (inet.Stream, error) {
150
return nil, err
151
}
152
134
- if err := protocol.WriteHeader(s, pid); err != nil {
135
- s.Close()
153
+ logStream := mstream.WrapStream(s, pid, h.bwc)
154
+
155
+ if err := protocol.WriteHeader(logStream, pid); err != nil {
156
+ logStream.Close()
157
return nil, err
158
}
159
139
- return s, nil
160
+ return logStream, nil
161
}
162
163
// Connect ensures there is a connection between this host and the peer with
@@ -210,3 +231,7 @@ func (h *BasicHost) Addrs() []ma.Multiaddr {
231
func (h *BasicHost) Close() error {
232
return h.proc.Close()
233
}
234
+
235
+func (h *BasicHost) GetBandwidthReporter() metrics.Reporter {
236
+ return h.bwc
237
+}
p2p/host/host.go
+3
@@ -3,6 +3,7 @@ package host
3
import (
4
ma "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr"
5
context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context"
6
+ metrics "github.com/ipfs/go-ipfs/metrics"
7
inet "github.com/ipfs/go-ipfs/p2p/net"
8
peer "github.com/ipfs/go-ipfs/p2p/peer"
9
protocol "github.com/ipfs/go-ipfs/p2p/protocol"
@@ -57,4 +58,6 @@ type Host interface {
58
59
// Close shuts down the host, its Network, and services.
60
Close() error
61
+
62
+ GetBandwidthReporter() metrics.Reporter
63
}
p2p/host/routed/routed.go
+5
@@ -9,6 +9,7 @@ import (
9
eventlog "github.com/ipfs/go-ipfs/thirdparty/eventlog"
10
lgbl "github.com/ipfs/go-ipfs/util/eventlog/loggables"
11
12
+ metrics "github.com/ipfs/go-ipfs/metrics"
13
host "github.com/ipfs/go-ipfs/p2p/host"
14
inet "github.com/ipfs/go-ipfs/p2p/net"
15
peer "github.com/ipfs/go-ipfs/p2p/peer"
@@ -115,3 +116,7 @@ func (rh *RoutedHost) Close() error {
116
// no need to close IpfsRouting. we dont own it.
117
return rh.host.Close()
118
}
119
+
120
+func (rh *RoutedHost) GetBandwidthReporter() metrics.Reporter {
121
+ return rh.host.GetBandwidthReporter()
122
+}
p2p/net/conn/dial.go
+4
@@ -50,6 +50,10 @@ func (d *Dialer) Dial(ctx context.Context, raddr ma.Multiaddr, remote peer.ID) (
50
return
51
}
52
53
+ if d.Wrapper != nil {
54
+ maconn = d.Wrapper(maconn)
55
+ }
56
+
57
c, err := newSingleConn(ctx, d.LocalPeer, remote, maconn)
58
if err != nil {
59
maconn.Close()
p2p/net/conn/interface.go
+3
@@ -66,6 +66,9 @@ type Dialer struct {
66
// PrivateKey used to initialize a secure connection.
67
// Warning: if PrivateKey is nil, connection will not be secured.
68
PrivateKey ic.PrivKey
69
+
70
+ // Wrapper to wrap the raw connection (optional)
71
+ Wrapper func(manet.Conn) manet.Conn
72
}
73
74
// Listener is an object that can accept connections. It matches net.Listener
p2p/net/conn/listen.go
+20
@@ -16,6 +16,9 @@ import (
16
peer "github.com/ipfs/go-ipfs/p2p/peer"
17
)
18
19
+// ConnWrapper is any function that wraps a raw multiaddr connection
20
+type ConnWrapper func(manet.Conn) manet.Conn
21
+
22
// listener is an object that can accept connections. It implements Listener
23
type listener struct {
24
manet.Listener
@@ -23,6 +26,8 @@ type listener struct {
26
local peer.ID // LocalPeer is the identity of the local Peer
27
privk ic.PrivKey // private key to use to initialize secure conns
28
29
+ wrapper ConnWrapper
30
+
31
cg ctxgroup.ContextGroup
32
}
33
@@ -76,6 +81,11 @@ func (l *listener) Accept() (net.Conn, error) {
81
}
82
83
log.Debugf("listener %s got connection: %s <---> %s", l, maconn.LocalMultiaddr(), maconn.RemoteMultiaddr())
84
+ // If we have a wrapper func, wrap this conn
85
+ if l.wrapper != nil {
86
+ maconn = l.wrapper(maconn)
87
+ }
88
+
89
c, err := newSingleConn(ctx, l.local, "", maconn)
90
if err != nil {
91
if catcher.IsTemporary(err) {
@@ -143,6 +153,16 @@ func Listen(ctx context.Context, addr ma.Multiaddr, local peer.ID, sk ic.PrivKey
153
return l, nil
154
}
155
156
+type ListenerConnWrapper interface {
157
+ SetConnWrapper(ConnWrapper)
158
+}
159
+
160
+// SetConnWrapper assigns a maconn ConnWrapper to wrap all incoming
161
+// connections with. MUST be set _before_ calling `Accept()`
162
+func (l *listener) SetConnWrapper(cw ConnWrapper) {
163
+ l.wrapper = cw
164
+}
165
+
166
func manetListen(addr ma.Multiaddr) (manet.Listener, error) {
167
network, naddr, err := manet.DialArgs(addr)
168
if err != nil {
p2p/net/mock/mock_test.go
+6
-1
@@ -12,6 +12,7 @@ import (
12
protocol "github.com/ipfs/go-ipfs/p2p/protocol"
13
testutil "github.com/ipfs/go-ipfs/util/testutil"
14
15
+ detectrace "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-detect-race"
16
context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context"
17
)
18
@@ -358,8 +359,12 @@ func makePonger(st string) func(inet.Stream) {
359
}
360
361
func TestStreamsStress(t *testing.T) {
362
+ nnodes := 100
363
+ if detectrace.WithRace() {
364
+ nnodes = 50
365
+ }
366
362
- mn, err := FullMeshConnected(context.Background(), 100)
367
+ mn, err := FullMeshConnected(context.Background(), nnodes)
368
if err != nil {
369
t.Fatal(err)
370
}
p2p/net/swarm/swarm.go
+5
-2
@@ -7,6 +7,7 @@ import (
7
"sync"
8
"time"
9
10
+ metrics "github.com/ipfs/go-ipfs/metrics"
11
inet "github.com/ipfs/go-ipfs/p2p/net"
12
addrutil "github.com/ipfs/go-ipfs/p2p/net/swarm/addr"
13
peer "github.com/ipfs/go-ipfs/p2p/peer"
@@ -42,12 +43,13 @@ type Swarm struct {
43
notifmu sync.RWMutex
44
notifs map[inet.Notifiee]ps.Notifiee
45
45
- cg ctxgroup.ContextGroup
46
+ cg ctxgroup.ContextGroup
47
+ bwc metrics.Reporter
48
}
49
50
// NewSwarm constructs a Swarm, with a Chan.
51
func NewSwarm(ctx context.Context, listenAddrs []ma.Multiaddr,
50
- local peer.ID, peers peer.Peerstore) (*Swarm, error) {
52
+ local peer.ID, peers peer.Peerstore, bwc metrics.Reporter) (*Swarm, error) {
53
54
listenAddrs, err := filterAddrs(listenAddrs)
55
if err != nil {
@@ -61,6 +63,7 @@ func NewSwarm(ctx context.Context, listenAddrs []ma.Multiaddr,
63
cg: ctxgroup.WithContext(ctx),
64
dialT: DialTimeout,
65
notifs: make(map[inet.Notifiee]ps.Notifiee),
66
+ bwc: bwc,
67
}
68
69
// configure Swarm
p2p/net/swarm/swarm_addr_test.go
+3
-2
@@ -3,6 +3,7 @@ package swarm
3
import (
4
"testing"
5
6
+ metrics "github.com/ipfs/go-ipfs/metrics"
7
addrutil "github.com/ipfs/go-ipfs/p2p/net/swarm/addr"
8
peer "github.com/ipfs/go-ipfs/p2p/peer"
9
testutil "github.com/ipfs/go-ipfs/util/testutil"
@@ -65,11 +66,11 @@ func TestFilterAddrs(t *testing.T) {
66
ps := peer.NewPeerstore()
67
ctx := context.Background()
68
68
- if _, err := NewNetwork(ctx, bad, id, ps); err == nil {
69
+ if _, err := NewNetwork(ctx, bad, id, ps, metrics.NewBandwidthCounter()); err == nil {
70
t.Fatal("should have failed to create swarm")
71
}
72
72
- if _, err := NewNetwork(ctx, goodAndBad, id, ps); err != nil {
73
+ if _, err := NewNetwork(ctx, goodAndBad, id, ps, metrics.NewBandwidthCounter()); err != nil {
74
t.Fatal("should have succeeded in creating swarm", err)
75
}
76
}
p2p/net/swarm/swarm_dial.go
+4
@@ -8,6 +8,7 @@ import (
8
"sync"
9
"time"
10
11
+ mconn "github.com/ipfs/go-ipfs/metrics/conn"
12
conn "github.com/ipfs/go-ipfs/p2p/net/conn"
13
addrutil "github.com/ipfs/go-ipfs/p2p/net/swarm/addr"
14
peer "github.com/ipfs/go-ipfs/p2p/peer"
@@ -318,6 +319,9 @@ func (s *Swarm) dial(ctx context.Context, p peer.ID) (*Conn, error) {
319
LocalPeer: s.local,
320
LocalAddrs: localAddrs,
321
PrivateKey: sk,
322
+ Wrapper: func(c manet.Conn) manet.Conn {
323
+ return mconn.WrapConn(s.bwc, c)
324
+ },
325
}
326
327
// try to get a connection to any addr
p2p/net/swarm/swarm_listen.go
+8
@@ -3,12 +3,14 @@ package swarm
3
import (
4
"fmt"
5
6
+ mconn "github.com/ipfs/go-ipfs/metrics/conn"
7
inet "github.com/ipfs/go-ipfs/p2p/net"
8
conn "github.com/ipfs/go-ipfs/p2p/net/conn"
9
addrutil "github.com/ipfs/go-ipfs/p2p/net/swarm/addr"
10
lgbl "github.com/ipfs/go-ipfs/util/eventlog/loggables"
11
12
ma "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr"
13
+ manet "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr-net"
14
ps "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-peerstream"
15
context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context"
16
multierr "github.com/ipfs/go-ipfs/thirdparty/multierr"
@@ -67,6 +69,12 @@ func (s *Swarm) setupListener(maddr ma.Multiaddr) error {
69
return err
70
}
71
72
+ if cw, ok := list.(conn.ListenerConnWrapper); ok {
73
+ cw.SetConnWrapper(func(c manet.Conn) manet.Conn {
74
+ return mconn.WrapConn(s.bwc, c)
75
+ })
76
+ }
77
+
78
// AddListener to the peerstream Listener. this will begin accepting connections
79
// and streams!
80
sl, err := s.swarm.AddListener(list)
p2p/net/swarm/swarm_net.go
+3
-2
@@ -5,6 +5,7 @@ import (
5
6
peer "github.com/ipfs/go-ipfs/p2p/peer"
7
8
+ metrics "github.com/ipfs/go-ipfs/metrics"
9
inet "github.com/ipfs/go-ipfs/p2p/net"
10
11
ctxgroup "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-ctxgroup"
@@ -19,9 +20,9 @@ type Network Swarm
20
21
// NewNetwork constructs a new network and starts listening on given addresses.
22
func NewNetwork(ctx context.Context, listen []ma.Multiaddr, local peer.ID,
22
- peers peer.Peerstore) (*Network, error) {
23
+ peers peer.Peerstore, bwc metrics.Reporter) (*Network, error) {
24
24
- s, err := NewSwarm(ctx, listen, local, peers)
25
+ s, err := NewSwarm(ctx, listen, local, peers, bwc)
26
if err != nil {
27
return nil, err
28
}
p2p/net/swarm/swarm_test.go
+2
-1
@@ -7,6 +7,7 @@ import (
7
"testing"
8
"time"
9
10
+ metrics "github.com/ipfs/go-ipfs/metrics"
11
inet "github.com/ipfs/go-ipfs/p2p/net"
12
peer "github.com/ipfs/go-ipfs/p2p/peer"
13
errors "github.com/ipfs/go-ipfs/util/debugerror"
@@ -58,7 +59,7 @@ func makeSwarms(ctx context.Context, t *testing.T, num int) []*Swarm {
59
peerstore.AddPrivKey(localnp.ID, localnp.PrivKey)
60
61
addrs := []ma.Multiaddr{localnp.Addr}
61
- swarm, err := NewSwarm(ctx, addrs, localnp.ID, peerstore)
62
+ swarm, err := NewSwarm(ctx, addrs, localnp.ID, peerstore, metrics.NewBandwidthCounter())
63
if err != nil {
64
t.Fatal(err)
65
}
p2p/protocol/identify/id.go
+6
@@ -9,6 +9,7 @@ import (
9
ma "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr"
10
context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context"
11
12
+ mstream "github.com/ipfs/go-ipfs/metrics/stream"
13
host "github.com/ipfs/go-ipfs/p2p/host"
14
inet "github.com/ipfs/go-ipfs/p2p/net"
15
peer "github.com/ipfs/go-ipfs/p2p/peer"
@@ -80,6 +81,8 @@ func (ids *IDService) IdentifyConn(c inet.Conn) {
81
log.Debugf("error opening initial stream for %s", ID)
82
log.Event(context.TODO(), "IdentifyOpenFailed", c.RemotePeer())
83
} else {
84
+ bwc := ids.Host.GetBandwidthReporter()
85
+ s = mstream.WrapStream(s, ID, bwc)
86
87
// ok give the response to our handler.
88
if err := protocol.WriteHeader(s, ID); err != nil {
@@ -106,6 +109,9 @@ func (ids *IDService) RequestHandler(s inet.Stream) {
109
defer s.Close()
110
c := s.Conn()
111
112
+ bwc := ids.Host.GetBandwidthReporter()
113
+ s = mstream.WrapStream(s, ID, bwc)
114
+
115
w := ggio.NewDelimitedWriter(s)
116
mes := pb.Identify{}
117
ids.populateMessage(&mes, s.Conn())
p2p/protocol/mux.go
+3
-3
@@ -45,9 +45,9 @@ func (m *Mux) Protocols() []ID {
45
return l
46
}
47
48
-// readHeader reads the stream and returns the next Handler function
48
+// ReadHeader reads the stream and returns the next Handler function
49
// according to the muxer encoding.
50
-func (m *Mux) readHeader(s io.Reader) (ID, inet.StreamHandler, error) {
50
+func (m *Mux) ReadHeader(s io.Reader) (ID, inet.StreamHandler, error) {
51
p, err := ReadHeader(s)
52
if err != nil {
53
return "", nil, err
@@ -110,7 +110,7 @@ func (m *Mux) Handle(s inet.Stream) {
110
func (m *Mux) HandleSync(s inet.Stream) {
111
ctx := context.Background()
112
113
- name, handler, err := m.readHeader(s)
113
+ name, handler, err := m.ReadHeader(s)
114
if err != nil {
115
err = fmt.Errorf("protocol mux error: %s", err)
116
log.Event(ctx, "muxError", lgbl.Error(err))
p2p/test/util/util.go
+2
-1
@@ -3,6 +3,7 @@ package testutil
3
import (
4
"testing"
5
6
+ metrics "github.com/ipfs/go-ipfs/metrics"
7
bhost "github.com/ipfs/go-ipfs/p2p/host/basic"
8
inet "github.com/ipfs/go-ipfs/p2p/net"
9
swarm "github.com/ipfs/go-ipfs/p2p/net/swarm"
@@ -18,7 +19,7 @@ func GenSwarmNetwork(t *testing.T, ctx context.Context) *swarm.Network {
19
ps := peer.NewPeerstore()
20
ps.AddPubKey(p.ID, p.PubKey)
21
ps.AddPrivKey(p.ID, p.PrivKey)
21
- n, err := swarm.NewNetwork(ctx, []ma.Multiaddr{p.Addr}, p.ID, ps)
22
+ n, err := swarm.NewNetwork(ctx, []ma.Multiaddr{p.Addr}, p.ID, ps, metrics.NewBandwidthCounter())
23
if err != nil {
24
t.Fatal(err)
25
}