func (rt *Rates) TotalRate() float64 {
	rt.mu.Lock()
	defer rt.mu.Unlock()
	return rt.totalRate
}
TotalRate returns the current total rate (counted across categories).
package stats
import (
	"encoding/json"
	"math"
	"sync"
	"time"
)
var timeNow = time.Now
type CountTracker interface {
	Counts() map[string]int64
}
type Rates struct {
	mu           sync.Mutex
	timeStamps   *RingInt64
	counts       map[string]*RingInt64
	countTracker CountTracker
	samples      int
	interval     time.Duration
	previousTotalCount int64
	timestampLastSampling time.Time
	totalRate float64
	closed    chan bool
	wg        sync.WaitGroup
}
func NewRates(name string, countTracker CountTracker, samples int, interval time.Duration) *Rates {
	if interval < 1*time.Second && interval != -1*time.Second {
		panic("interval too small")
	}
	rt := &Rates{
		timeStamps:            NewRingInt64(samples + 1),
		counts:                make(map[string]*RingInt64),
		countTracker:          countTracker,
		samples:               samples + 1,
		interval:              interval,
		timestampLastSampling: timeNow(),
		closed:                make(chan bool),
	}
	if name != "" {
		publish(name, rt)
	}
	if interval > 0 {
		rt.wg.Add(1)
		go rt.track()
	}
	return rt
}
func (rt *Rates) Close() {
	close(rt.closed)
	rt.wg.Wait()
}
func (rt *Rates) track() {
	defer rt.wg.Done()
	for {
		rt.snapshot()
		select {
		case <-rt.closed:
			return
		case <-time.After(rt.interval):
		}
	}
}
func (rt *Rates) snapshot() {
	rt.mu.Lock()
	defer rt.mu.Unlock()
	now := timeNow()
	rt.timeStamps.Add(now.UnixNano())
	var totalCount int64
	for k, v := range rt.countTracker.Counts() {
		if k != "All" {
			totalCount += v
		}
		if values, ok := rt.counts[k]; ok {
			values.Add(v)
		} else {
			rt.counts[k] = NewRingInt64(rt.samples)
			rt.counts[k].Add(0)
			rt.counts[k].Add(v)
		}
	}
	durationSeconds := now.Sub(rt.timestampLastSampling).Seconds()
	rate := float64(totalCount-rt.previousTotalCount) / durationSeconds
	rt.totalRate = math.Floor(rate*10+0.5) / 10
	rt.previousTotalCount = totalCount
	rt.timestampLastSampling = now
}
func (rt *Rates) Get() (rateMap map[string][]float64) {
	rt.mu.Lock()
	defer rt.mu.Unlock()
	rateMap = make(map[string][]float64)
	timeStamps := rt.timeStamps.Values()
	if len(timeStamps) <= 1 {
		return
	}
	for k, v := range rt.counts {
		rateMap[k] = make([]float64, len(timeStamps)-1)
		values := v.Values()
		valueIndex := len(values) - 1
		for i := len(timeStamps) - 1; i > 0; i-- {
			if valueIndex <= 0 {
				rateMap[k][i-1] = 0
				continue
			}
			elapsed := float64((timeStamps[i] - timeStamps[i-1]) / 1e9)
			rateMap[k][i-1] = float64(values[valueIndex]-values[valueIndex-1]) / elapsed
			valueIndex--
		}
	}
	return
}