106 lines
2.1 KiB
Go
106 lines
2.1 KiB
Go
package stats
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
type timeseries struct {
|
|
sync.Mutex
|
|
now time.Time
|
|
size int
|
|
interval time.Duration
|
|
total metric
|
|
samples []metric
|
|
}
|
|
|
|
func (ts *timeseries) Reset() {
|
|
ts.total.Reset()
|
|
for _, s := range ts.samples {
|
|
s.Reset()
|
|
}
|
|
}
|
|
|
|
func (ts *timeseries) roll() {
|
|
t := time.Now()
|
|
roll := int((t.Round(ts.interval).Sub(ts.now.Round(ts.interval))) / ts.interval)
|
|
ts.now = t
|
|
n := len(ts.samples)
|
|
if roll <= 0 {
|
|
return
|
|
}
|
|
if roll >= len(ts.samples) {
|
|
ts.Reset()
|
|
} else {
|
|
for i := 0; i < roll; i++ {
|
|
tmp := ts.samples[n-1]
|
|
for j := n - 1; j > 0; j-- {
|
|
ts.samples[j] = ts.samples[j-1]
|
|
}
|
|
ts.samples[0] = tmp
|
|
ts.samples[0].Reset()
|
|
}
|
|
ts.total.Aggregate(roll, ts.samples)
|
|
}
|
|
}
|
|
|
|
func (ts *timeseries) Add(n float64) {
|
|
ts.Lock()
|
|
defer ts.Unlock()
|
|
ts.roll()
|
|
ts.total.Add(n)
|
|
ts.samples[0].Add(n)
|
|
}
|
|
|
|
func (ts *timeseries) MarshalJSON() ([]byte, error) {
|
|
ts.Lock()
|
|
defer ts.Unlock()
|
|
ts.roll()
|
|
return json.Marshal(struct {
|
|
Interval float64 `json:"interval"`
|
|
Total Metric `json:"total"`
|
|
Samples []metric `json:"samples"`
|
|
}{float64(ts.interval) / float64(time.Second), ts.total, ts.samples})
|
|
}
|
|
|
|
func (ts *timeseries) String() string {
|
|
ts.Lock()
|
|
defer ts.Unlock()
|
|
ts.roll()
|
|
return ts.total.String()
|
|
}
|
|
|
|
func newTimeseries(builder func() metric, frame string) *timeseries {
|
|
var (
|
|
totalNum, intervalNum int
|
|
totalUnit, intervalUnit rune
|
|
)
|
|
units := map[rune]time.Duration{
|
|
's': time.Second,
|
|
'm': time.Minute,
|
|
'h': time.Hour,
|
|
'd': time.Hour * 24,
|
|
'w': time.Hour * 24 * 7,
|
|
'M': time.Hour * 24 * 30,
|
|
'y': time.Hour * 24 * 365,
|
|
}
|
|
fmt.Sscanf(frame, "%d%c%d%c", &totalNum, &totalUnit, &intervalNum, &intervalUnit)
|
|
interval := units[intervalUnit] * time.Duration(intervalNum)
|
|
if interval == 0 {
|
|
interval = time.Minute
|
|
}
|
|
totalDuration := units[totalUnit] * time.Duration(totalNum)
|
|
if totalDuration == 0 {
|
|
totalDuration = interval * 15
|
|
}
|
|
n := int(totalDuration / interval)
|
|
samples := make([]metric, n, n)
|
|
for i := 0; i < n; i++ {
|
|
samples[i] = builder()
|
|
}
|
|
totalMetric := builder()
|
|
return ×eries{interval: interval, total: totalMetric, samples: samples}
|
|
}
|