123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131 |
- package metrics
- import (
- "fmt"
- "net/http"
- "sort"
- "time"
- )
- // MetricsSummary holds a roll-up of metrics info for a given interval
- type MetricsSummary struct {
- Timestamp string
- Gauges []GaugeValue
- Points []PointValue
- Counters []SampledValue
- Samples []SampledValue
- }
- type GaugeValue struct {
- Name string
- Hash string `json:"-"`
- Value float32
- Labels []Label `json:"-"`
- DisplayLabels map[string]string `json:"Labels"`
- }
- type PointValue struct {
- Name string
- Points []float32
- }
- type SampledValue struct {
- Name string
- Hash string `json:"-"`
- *AggregateSample
- Mean float64
- Stddev float64
- Labels []Label `json:"-"`
- DisplayLabels map[string]string `json:"Labels"`
- }
- // deepCopy allocates a new instance of AggregateSample
- func (source *SampledValue) deepCopy() SampledValue {
- dest := *source
- if source.AggregateSample != nil {
- dest.AggregateSample = &AggregateSample{}
- *dest.AggregateSample = *source.AggregateSample
- }
- return dest
- }
- // DisplayMetrics returns a summary of the metrics from the most recent finished interval.
- func (i *InmemSink) DisplayMetrics(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
- data := i.Data()
- var interval *IntervalMetrics
- n := len(data)
- switch {
- case n == 0:
- return nil, fmt.Errorf("no metric intervals have been initialized yet")
- case n == 1:
- // Show the current interval if it's all we have
- interval = data[0]
- default:
- // Show the most recent finished interval if we have one
- interval = data[n-2]
- }
- interval.RLock()
- defer interval.RUnlock()
- summary := MetricsSummary{
- Timestamp: interval.Interval.Round(time.Second).UTC().String(),
- Gauges: make([]GaugeValue, 0, len(interval.Gauges)),
- Points: make([]PointValue, 0, len(interval.Points)),
- }
- // Format and sort the output of each metric type, so it gets displayed in a
- // deterministic order.
- for name, points := range interval.Points {
- summary.Points = append(summary.Points, PointValue{name, points})
- }
- sort.Slice(summary.Points, func(i, j int) bool {
- return summary.Points[i].Name < summary.Points[j].Name
- })
- for hash, value := range interval.Gauges {
- value.Hash = hash
- value.DisplayLabels = make(map[string]string)
- for _, label := range value.Labels {
- value.DisplayLabels[label.Name] = label.Value
- }
- value.Labels = nil
- summary.Gauges = append(summary.Gauges, value)
- }
- sort.Slice(summary.Gauges, func(i, j int) bool {
- return summary.Gauges[i].Hash < summary.Gauges[j].Hash
- })
- summary.Counters = formatSamples(interval.Counters)
- summary.Samples = formatSamples(interval.Samples)
- return summary, nil
- }
- func formatSamples(source map[string]SampledValue) []SampledValue {
- output := make([]SampledValue, 0, len(source))
- for hash, sample := range source {
- displayLabels := make(map[string]string)
- for _, label := range sample.Labels {
- displayLabels[label.Name] = label.Value
- }
- output = append(output, SampledValue{
- Name: sample.Name,
- Hash: hash,
- AggregateSample: sample.AggregateSample,
- Mean: sample.AggregateSample.Mean(),
- Stddev: sample.AggregateSample.Stddev(),
- DisplayLabels: displayLabels,
- })
- }
- sort.Slice(output, func(i, j int) bool {
- return output[i].Hash < output[j].Hash
- })
- return output
- }
|