OSDN Git Service

new repo
[bytom/vapor.git] / metrics / metrics.go
1 // Package metrics provides convenient facilities to record
2 // on-line high-level performance metrics.
3 package metrics
4
5 import (
6         "bytes"
7         "encoding/json"
8         "expvar"
9         "fmt"
10         "sync"
11         "time"
12
13         "github.com/codahale/hdrhistogram"
14 )
15
16 // Period is the size of a RotatingLatency bucket.
17 // Each RotatingLatency will rotate once per Period.
18 const Period = time.Minute
19
20 var (
21         rotatingLatenciesMu sync.Mutex
22         rotatingLatencies   []*RotatingLatency
23         latencyExpvar       = expvar.NewMap("latency")
24 )
25
26 // PublishLatency publishes rl as an expvar inside the
27 // global latency map (which is itself published under
28 // the key "latency").
29 func PublishLatency(key string, rl *RotatingLatency) {
30         latencyExpvar.Set(key, rl)
31 }
32
33 // A Latency records information about the aggregate latency
34 // of an operation over time.
35 // Internally it holds an HDR histogram (to three significant figures)
36 // and a counter of attempts to record a value
37 // greater than the histogram's max.
38 type Latency struct {
39         limit time.Duration // readonly
40
41         time  time.Time
42         hdr   hdrhistogram.Histogram
43         nover int           // how many values were over limit
44         max   time.Duration // max recorded value (can be over limit)
45 }
46
47 // NewLatency returns a new latency histogram with the given
48 // duration limit and with three significant figures of precision.
49 func NewLatency(limit time.Duration) *Latency {
50         return &Latency{
51                 hdr:   *hdrhistogram.New(0, int64(limit), 2),
52                 limit: limit,
53         }
54 }
55
56 // Record attempts to record a duration in the histogram.
57 // If d is greater than the max allowed duration,
58 // it increments a counter instead.
59 func (l *Latency) Record(d time.Duration) {
60         if d > l.max {
61                 l.max = d
62         }
63         if d > l.limit {
64                 l.nover++
65         } else {
66                 l.hdr.RecordValue(int64(d))
67         }
68 }
69
70 // Reset resets l to is original empty state.
71 func (l *Latency) Reset() {
72         l.hdr.Reset()
73         l.nover = 0
74 }
75
76 // String returns l as a JSON string.
77 // This makes it suitable for use as an expvar.Val.
78 func (l *Latency) String() string {
79         var b bytes.Buffer
80         fmt.Fprintf(&b, `{"Histogram":`)
81         h, _ := json.Marshal((&l.hdr).Export()) // #nosec
82         b.Write(h)
83         fmt.Fprintf(&b, `,"Over":%d,"Timestamp":%d,"Max":%d}`, l.nover, l.time.Unix(), l.max)
84         return b.String()
85 }
86
87 // A RotatingLatency holds a rotating circular buffer of Latency objects,
88 // that rotates once per Period time.
89 // It can be used as an expvar Val.
90 // Its exported methods are safe to call concurrently.
91 type RotatingLatency struct {
92         mu  sync.Mutex
93         l   []Latency
94         n   int
95         cur *Latency
96 }
97
98 // NewRotatingLatency returns a new rotating latency recorder
99 // with n buckets of history.
100 func NewRotatingLatency(n int, max time.Duration) *RotatingLatency {
101         r := &RotatingLatency{
102                 l: make([]Latency, n),
103         }
104         for i := range r.l {
105                 r.l[i] = *NewLatency(max)
106         }
107         r.rotate()
108         rotatingLatenciesMu.Lock()
109         rotatingLatencies = append(rotatingLatencies, r)
110         rotatingLatenciesMu.Unlock()
111         return r
112 }
113
114 // Record attempts to record a duration in the current Latency in r.
115 // If d is greater than the max allowed duration,
116 // it increments a counter instead.
117 func (r *RotatingLatency) Record(d time.Duration) {
118         r.mu.Lock()
119         r.cur.Record(d)
120         r.mu.Unlock()
121 }
122
123 func (r *RotatingLatency) RecordSince(t0 time.Time) {
124         r.Record(time.Since(t0))
125 }
126
127 func (r *RotatingLatency) rotate() {
128         r.mu.Lock()
129         defer r.mu.Unlock()
130         if r.cur != nil {
131                 r.cur.time = time.Now()
132         }
133         r.n++
134         r.cur = &r.l[r.n%len(r.l)]
135         r.cur.Reset()
136 }
137
138 // String returns r as a JSON string.
139 // This makes it suitable for use as an expvar.Val.
140 //
141 // Example:
142 //
143 //  {
144 //      "NumRot": 204,
145 //      "Buckets": [
146 //          {
147 //              "Over": 4,
148 //              "Histogram": {
149 //                  "LowestTrackableValue": 0,
150 //                  "HighestTrackableValue": 1000000000,
151 //                  "SignificantFigures": 2,
152 //                  "Counts": [2,0,15,...]
153 //              }
154 //          },
155 //          ...
156 //      ]
157 //  }
158 //
159 // Note that the last bucket is actively recording values.
160 // To collect complete and accurate data over a long time,
161 // store the next-to-last bucket after each rotation.
162 // The last bucket is only useful for a "live" view
163 // with finer granularity than the rotation period (which is one minute).
164 func (r *RotatingLatency) String() string {
165         r.mu.Lock()
166         defer r.mu.Unlock()
167         var b bytes.Buffer
168         fmt.Fprintf(&b, `{"Buckets":[`)
169         for i := range r.l {
170                 if i > 0 {
171                         b.WriteByte(',')
172                 }
173                 j := (r.n + i + 1) % len(r.l)
174                 fmt.Fprintf(&b, "%s", &r.l[j])
175         }
176         fmt.Fprintf(&b, `],"NumRot":%d}`, r.n)
177         return b.String()
178 }
179
180 func init() {
181         go func() {
182                 for range time.Tick(Period) {
183                         rotatingLatenciesMu.Lock()
184                         a := rotatingLatencies
185                         rotatingLatenciesMu.Unlock()
186                         for _, rot := range a {
187                                 rot.rotate()
188                         }
189                 }
190         }()
191 }