liuxiaolong
2022-06-28 37714b1093c04061e636e5b1d27179652e671c0a
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
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"`
}
 
// 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 = i.intervals[0]
    default:
        // Show the most recent finished interval if we have one
        interval = i.intervals[n-2]
    }
 
    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
}