Package list golang-github-go-kit-kit / 8aab2ed
Distribution shall also return quantile counts Peter Bourgon 5 years ago
7 changed file(s) with 72 addition(s) and 20 deletion(s). Raw diff Collapse all Expand all
1818 import (
1919 "expvar"
2020 "fmt"
21 "sort"
2122 "strconv"
2223 "sync"
2324 "time"
6465 func (g *gauge) With(metrics.Field) metrics.Gauge { return g }
6566 func (g *gauge) Add(delta float64) { g.v.Add(delta) }
6667 func (g *gauge) Set(value float64) { g.v.Set(value) }
68 func (g *gauge) Get() float64 { return mustParseFloat64(g.v.String()) }
6769
6870 // PublishCallbackGauge publishes a Gauge as an expvar with the given name,
6971 // whose value is determined at collect time by the passed callback function.
125127 }
126128 }
127129
128 func (h *histogram) Distribution() []metrics.Bucket {
129 bars := h.hist.Current.Distribution()
130 func (h *histogram) Distribution() ([]metrics.Bucket, []metrics.Quantile) {
131 bars := h.hist.Merge().Distribution()
130132 buckets := make([]metrics.Bucket, len(bars))
131133 for i, bar := range bars {
132134 buckets[i] = metrics.Bucket{
135137 Count: bar.Count,
136138 }
137139 }
138 return buckets
140 quantiles := make([]metrics.Quantile, 0, len(h.gauges))
141 for quantile, gauge := range h.gauges {
142 quantiles = append(quantiles, metrics.Quantile{
143 Quantile: quantile,
144 Value: int64(gauge.Get()),
145 })
146 }
147 sort.Sort(quantileSlice(quantiles))
148 return buckets, quantiles
139149 }
140150
141151 func (h *histogram) rotateLoop(d time.Duration) {
145155 h.mu.Unlock()
146156 }
147157 }
158
159 func mustParseFloat64(s string) float64 {
160 f, err := strconv.ParseFloat(s, 64)
161 if err != nil {
162 panic(err)
163 }
164 return f
165 }
166
167 type quantileSlice []metrics.Quantile
168
169 func (a quantileSlice) Len() int { return len(a) }
170 func (a quantileSlice) Less(i, j int) bool { return a[i].Quantile < a[j].Quantile }
171 func (a quantileSlice) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
2020 With(Field) Gauge
2121 Set(value float64)
2222 Add(delta float64)
23 Get() float64
2324 }
2425
2526 // Histogram tracks the distribution of a stream of values (e.g. the number of
2930 Name() string
3031 With(Field) Histogram
3132 Observe(value int64)
32 Distribution() []Bucket
33 Distribution() ([]Bucket, []Quantile)
3334 }
3435
3536 // Field is a key/value pair associated with an observation for a specific
4546 To int64
4647 Count int64
4748 }
49
50 // Quantile is a pair of quantile (0..100) and its observed maximum value.
51 type Quantile struct {
52 Quantile int // 0..100
53 Value int64
54 }
6969 }
7070 }
7171
72 func (g multiGauge) Get() float64 {
73 panic("cannot call Get on a MultiGauge")
74 }
75
7276 type multiHistogram struct {
7377 name string
7478 a []Histogram
101105 }
102106 }
103107
104 func (h multiHistogram) Distribution() []Bucket {
105 return []Bucket{} // TODO(pb): can this be statistically valid?
108 func (h multiHistogram) Distribution() ([]Bucket, []Quantile) {
109 // TODO(pb): there may be a way to do this
110 panic("cannot call Distribution on a MultiHistogram")
106111 }
1212
1313 // PrintDistribution writes a human-readable graph of the distribution to the
1414 // passed writer.
15 func PrintDistribution(w io.Writer, name string, buckets []Bucket) {
16 fmt.Fprintf(w, "name: %v\n", name)
15 func PrintDistribution(w io.Writer, h Histogram) {
16 buckets, quantiles := h.Distribution()
17
18 fmt.Fprintf(w, "name: %v\n", h.Name())
19 fmt.Fprintf(w, "quantiles: %v\n", quantiles)
1720
1821 var total float64
1922 for _, bucket := range buckets {
1212
1313 func TestPrintDistribution(t *testing.T) {
1414 var (
15 name = "foobar"
1615 quantiles = []int{50, 90, 95, 99}
17 h = expvar.NewHistogram("test_print_distribution", 1, 10, 3, quantiles...)
16 h = expvar.NewHistogram("test_print_distribution", 0, 100, 3, quantiles...)
1817 seed = int64(555)
1918 mean = int64(5)
2019 stdev = int64(1)
2221 teststat.PopulateNormalHistogram(t, h, seed, mean, stdev)
2322
2423 var buf bytes.Buffer
25 metrics.PrintDistribution(&buf, name, h.Distribution())
24 metrics.PrintDistribution(&buf, h)
2625 t.Logf("\n%s\n", buf.String())
2726
2827 // Count the number of bar chart characters.
29 // We should have roughly 100 in any distribution.
28 // We should have ca. 100 in any distribution with a small-enough stdev.
3029
3130 var n int
3231 for _, r := range buf.String() {
8383
8484 func (g prometheusGauge) Add(delta float64) {
8585 g.GaugeVec.With(prometheus.Labels(g.Pairs)).Add(delta)
86 }
87
88 func (g prometheusGauge) Get() float64 {
89 // TODO(pb): see https://github.com/prometheus/client_golang/issues/58
90 return 0.0
8691 }
8792
8893 // RegisterCallbackGauge registers a Gauge with Prometheus whose value is
128133 s.SummaryVec.With(prometheus.Labels(s.Pairs)).Observe(float64(value))
129134 }
130135
131 func (s prometheusSummary) Distribution() []metrics.Bucket {
136 func (s prometheusSummary) Distribution() ([]metrics.Bucket, []metrics.Quantile) {
132137 // TODO(pb): see https://github.com/prometheus/client_golang/issues/58
133 return []metrics.Bucket{}
138 return []metrics.Bucket{}, []metrics.Quantile{}
134139 }
135140
136141 type prometheusHistogram struct {
168173 h.HistogramVec.With(prometheus.Labels(h.Pairs)).Observe(float64(value))
169174 }
170175
171 func (h prometheusHistogram) Distribution() []metrics.Bucket {
176 func (h prometheusHistogram) Distribution() ([]metrics.Bucket, []metrics.Quantile) {
172177 // TODO(pb): see https://github.com/prometheus/client_golang/issues/58
173 return []metrics.Bucket{}
178 return []metrics.Bucket{}, []metrics.Quantile{}
174179 }
175180
176181 func pairsFrom(fieldKeys []string) map[string]string {
1616 "fmt"
1717 "io"
1818 "log"
19 "math"
1920 "time"
21
22 "sync/atomic"
2023
2124 "github.com/go-kit/kit/metrics"
2225 )
5356 func (c *statsdCounter) Add(delta uint64) { c.c <- fmt.Sprintf("%d|c", delta) }
5457
5558 type statsdGauge struct {
56 key string
57 g chan string
59 key string
60 lastValue uint64 // math.Float64frombits
61 g chan string
5862 }
5963
6064 // NewGauge returns a Gauge that emits values in the statsd protocol to the
8690 }
8791
8892 func (g *statsdGauge) Set(value float64) {
93 atomic.StoreUint64(&g.lastValue, math.Float64bits(value))
8994 g.g <- fmt.Sprintf("%f|g", value)
95 }
96
97 func (g *statsdGauge) Get() float64 {
98 return math.Float64frombits(atomic.LoadUint64(&g.lastValue))
9099 }
91100
92101 // NewCallbackGauge emits values in the statsd protocol to the passed writer.
148157 h.h <- fmt.Sprintf("%d|ms", value)
149158 }
150159
151 func (h *statsdHistogram) Distribution() []metrics.Bucket {
160 func (h *statsdHistogram) Distribution() ([]metrics.Bucket, []metrics.Quantile) {
152161 // TODO(pb): no way to do this without introducing e.g. codahale/hdrhistogram
153 return []metrics.Bucket{}
162 return []metrics.Bucket{}, []metrics.Quantile{}
154163 }
155164
156165 var tick = time.Tick