blob: 504f1b374854b6716f7a10d5a8a2456a0f1b8387 [file] [log] [blame]
William Kurkianea869482019-04-09 15:16:11 -04001package metrics
2
3import (
4 "fmt"
5 "net/http"
6 "sort"
7 "time"
8)
9
10// MetricsSummary holds a roll-up of metrics info for a given interval
11type MetricsSummary struct {
12 Timestamp string
13 Gauges []GaugeValue
14 Points []PointValue
15 Counters []SampledValue
16 Samples []SampledValue
17}
18
19type GaugeValue struct {
20 Name string
21 Hash string `json:"-"`
22 Value float32
23
24 Labels []Label `json:"-"`
25 DisplayLabels map[string]string `json:"Labels"`
26}
27
28type PointValue struct {
29 Name string
30 Points []float32
31}
32
33type SampledValue struct {
34 Name string
35 Hash string `json:"-"`
36 *AggregateSample
37 Mean float64
38 Stddev float64
39
40 Labels []Label `json:"-"`
41 DisplayLabels map[string]string `json:"Labels"`
42}
43
44// DisplayMetrics returns a summary of the metrics from the most recent finished interval.
45func (i *InmemSink) DisplayMetrics(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
46 data := i.Data()
47
48 var interval *IntervalMetrics
49 n := len(data)
50 switch {
51 case n == 0:
52 return nil, fmt.Errorf("no metric intervals have been initialized yet")
53 case n == 1:
54 // Show the current interval if it's all we have
55 interval = i.intervals[0]
56 default:
57 // Show the most recent finished interval if we have one
58 interval = i.intervals[n-2]
59 }
60
61 summary := MetricsSummary{
62 Timestamp: interval.Interval.Round(time.Second).UTC().String(),
63 Gauges: make([]GaugeValue, 0, len(interval.Gauges)),
64 Points: make([]PointValue, 0, len(interval.Points)),
65 }
66
67 // Format and sort the output of each metric type, so it gets displayed in a
68 // deterministic order.
69 for name, points := range interval.Points {
70 summary.Points = append(summary.Points, PointValue{name, points})
71 }
72 sort.Slice(summary.Points, func(i, j int) bool {
73 return summary.Points[i].Name < summary.Points[j].Name
74 })
75
76 for hash, value := range interval.Gauges {
77 value.Hash = hash
78 value.DisplayLabels = make(map[string]string)
79 for _, label := range value.Labels {
80 value.DisplayLabels[label.Name] = label.Value
81 }
82 value.Labels = nil
83
84 summary.Gauges = append(summary.Gauges, value)
85 }
86 sort.Slice(summary.Gauges, func(i, j int) bool {
87 return summary.Gauges[i].Hash < summary.Gauges[j].Hash
88 })
89
90 summary.Counters = formatSamples(interval.Counters)
91 summary.Samples = formatSamples(interval.Samples)
92
93 return summary, nil
94}
95
96func formatSamples(source map[string]SampledValue) []SampledValue {
97 output := make([]SampledValue, 0, len(source))
98 for hash, sample := range source {
99 displayLabels := make(map[string]string)
100 for _, label := range sample.Labels {
101 displayLabels[label.Name] = label.Value
102 }
103
104 output = append(output, SampledValue{
105 Name: sample.Name,
106 Hash: hash,
107 AggregateSample: sample.AggregateSample,
108 Mean: sample.AggregateSample.Mean(),
109 Stddev: sample.AggregateSample.Stddev(),
110 DisplayLabels: displayLabels,
111 })
112 }
113 sort.Slice(output, func(i, j int) bool {
114 return output[i].Hash < output[j].Hash
115 })
116
117 return output
118}