Checkpoint
This commit is contained in:
104
stats/stats.go
Normal file
104
stats/stats.go
Normal file
@@ -0,0 +1,104 @@
|
||||
package stats
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"math"
|
||||
"sort"
|
||||
"strconv"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
// Metric is a single meter (counter, gauge or histogram, optionally - with history)
|
||||
type Metric interface {
|
||||
Add(n float64)
|
||||
String() string
|
||||
}
|
||||
|
||||
// metric is an extended private interface with some additional internal
|
||||
// methods used by timeseries. Counters, gauges and histograms implement it.
|
||||
type metric interface {
|
||||
Metric
|
||||
Reset()
|
||||
Aggregate(roll int, samples []metric)
|
||||
}
|
||||
|
||||
type multimetric []*timeseries
|
||||
|
||||
func (mm multimetric) Add(n float64) {
|
||||
for _, m := range mm {
|
||||
m.Add(n)
|
||||
}
|
||||
}
|
||||
|
||||
func (mm multimetric) MarshalJSON() ([]byte, error) {
|
||||
b := []byte(`{"metrics":[`)
|
||||
for i, m := range mm {
|
||||
if i != 0 {
|
||||
b = append(b, ',')
|
||||
}
|
||||
x, _ := json.Marshal(m)
|
||||
b = append(b, x...)
|
||||
}
|
||||
b = append(b, ']', '}')
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func (mm multimetric) String() string {
|
||||
return mm[len(mm)-1].String()
|
||||
}
|
||||
|
||||
func newMetric(builder func() metric, frames ...string) Metric {
|
||||
if len(frames) == 0 {
|
||||
return builder()
|
||||
}
|
||||
if len(frames) == 1 {
|
||||
return newTimeseries(builder, frames[0])
|
||||
}
|
||||
mm := multimetric{}
|
||||
for _, frame := range frames {
|
||||
mm = append(mm, newTimeseries(builder, frame))
|
||||
}
|
||||
sort.Slice(mm, func(i, j int) bool {
|
||||
a, b := mm[i], mm[j]
|
||||
return a.interval.Seconds()*float64(len(a.samples)) < b.interval.Seconds()*float64(len(b.samples))
|
||||
})
|
||||
return mm
|
||||
}
|
||||
|
||||
// NewCounter returns a counter metric that increments the value with each
|
||||
// incoming number.
|
||||
func NewCounter(frames ...string) Metric {
|
||||
return newMetric(func() metric { return &counter{} }, frames...)
|
||||
}
|
||||
|
||||
type counter struct {
|
||||
count uint64
|
||||
}
|
||||
|
||||
func (c *counter) String() string { return strconv.FormatFloat(c.value(), 'g', -1, 64) }
|
||||
func (c *counter) Reset() { atomic.StoreUint64(&c.count, math.Float64bits(0)) }
|
||||
func (c *counter) value() float64 { return math.Float64frombits(atomic.LoadUint64(&c.count)) }
|
||||
|
||||
func (c *counter) Add(n float64) {
|
||||
for {
|
||||
old := math.Float64frombits(atomic.LoadUint64(&c.count))
|
||||
new := old + n
|
||||
if atomic.CompareAndSwapUint64(&c.count, math.Float64bits(old), math.Float64bits(new)) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *counter) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(struct {
|
||||
Type string `json:"type"`
|
||||
Count float64 `json:"count"`
|
||||
}{"c", c.value()})
|
||||
}
|
||||
|
||||
func (c *counter) Aggregate(roll int, samples []metric) {
|
||||
c.Reset()
|
||||
for _, s := range samples {
|
||||
c.Add(s.(*counter).value())
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user