forked from jshiffer/matterbridge
113 lines
2.7 KiB
Go
113 lines
2.7 KiB
Go
|
package stats
|
||
|
|
||
|
import "fmt"
|
||
|
|
||
|
// Type is the type of aggregation of apply
|
||
|
type Type int
|
||
|
|
||
|
const (
|
||
|
AggregateAvg Type = iota
|
||
|
AggregateSum
|
||
|
AggregateHistogram
|
||
|
)
|
||
|
|
||
|
var (
|
||
|
// HistogramPercentiles is used to determine which percentiles to return for
|
||
|
// SimpleCounter.Aggregate
|
||
|
HistogramPercentiles = map[string]float64{
|
||
|
"p50": 0.5,
|
||
|
"p95": 0.95,
|
||
|
"p99": 0.99,
|
||
|
}
|
||
|
|
||
|
// MinSamplesForPercentiles is used by SimpleCounter.Aggregate to determine
|
||
|
// what the minimum number of samples is required for percentile analysis
|
||
|
MinSamplesForPercentiles = 10
|
||
|
)
|
||
|
|
||
|
// Aggregates can be used to merge counters together. This is not goroutine safe
|
||
|
type Aggregates map[string]Counter
|
||
|
|
||
|
// Add adds the counter for aggregation. This is not goroutine safe
|
||
|
func (a Aggregates) Add(c Counter) error {
|
||
|
key := c.FullKey()
|
||
|
if counter, ok := a[key]; ok {
|
||
|
if counter.GetType() != c.GetType() {
|
||
|
return fmt.Errorf("stats: mismatched aggregation type for: %s", key)
|
||
|
}
|
||
|
counter.AddValues(c.GetValues()...)
|
||
|
} else {
|
||
|
a[key] = c
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// Counter is the interface used by Aggregates to merge counters together
|
||
|
type Counter interface {
|
||
|
// FullKey is used to uniquely identify the counter
|
||
|
FullKey() string
|
||
|
|
||
|
// AddValues adds values for aggregation
|
||
|
AddValues(...float64)
|
||
|
|
||
|
// GetValues returns the values for aggregation
|
||
|
GetValues() []float64
|
||
|
|
||
|
// GetType returns the type of aggregation to apply
|
||
|
GetType() Type
|
||
|
}
|
||
|
|
||
|
// SimpleCounter is a basic implementation of the Counter interface
|
||
|
type SimpleCounter struct {
|
||
|
Key string
|
||
|
Values []float64
|
||
|
Type Type
|
||
|
}
|
||
|
|
||
|
// FullKey is part of the Counter interace
|
||
|
func (s *SimpleCounter) FullKey() string {
|
||
|
return s.Key
|
||
|
}
|
||
|
|
||
|
// GetValues is part of the Counter interface
|
||
|
func (s *SimpleCounter) GetValues() []float64 {
|
||
|
return s.Values
|
||
|
}
|
||
|
|
||
|
// AddValues is part of the Counter interface
|
||
|
func (s *SimpleCounter) AddValues(vs ...float64) {
|
||
|
s.Values = append(s.Values, vs...)
|
||
|
}
|
||
|
|
||
|
// GetType is part of the Counter interface
|
||
|
func (s *SimpleCounter) GetType() Type {
|
||
|
return s.Type
|
||
|
}
|
||
|
|
||
|
// Aggregate aggregates the provided values appropriately, returning a map
|
||
|
// from key to value. If AggregateHistogram is specified, the map will contain
|
||
|
// the relevant percentiles as specified by HistogramPercentiles
|
||
|
func (s *SimpleCounter) Aggregate() map[string]float64 {
|
||
|
switch s.Type {
|
||
|
case AggregateAvg:
|
||
|
return map[string]float64{
|
||
|
s.Key: Average(s.Values),
|
||
|
}
|
||
|
case AggregateSum:
|
||
|
return map[string]float64{
|
||
|
s.Key: Sum(s.Values),
|
||
|
}
|
||
|
case AggregateHistogram:
|
||
|
histogram := map[string]float64{
|
||
|
s.Key: Average(s.Values),
|
||
|
}
|
||
|
if len(s.Values) > MinSamplesForPercentiles {
|
||
|
for k, v := range Percentiles(s.Values, HistogramPercentiles) {
|
||
|
histogram[fmt.Sprintf("%s.%s", s.Key, k)] = v
|
||
|
}
|
||
|
}
|
||
|
return histogram
|
||
|
}
|
||
|
panic("stats: unsupported aggregation type")
|
||
|
}
|