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")
 | |
| }
 |