// Quantiles expects a regex whose first capture group can be parsed as a // float64. It will dump the WriterTo and parse each line, expecting to find a // match. It observes all captured floats into a generic.Histogram with the // given number of buckets, and returns the 50th, 90th, 95th, and 99th quantiles // from that histogram. func Quantiles(w io.WriterTo, regex string, buckets int) func() (float64, float64, float64, float64) { return func() (float64, float64, float64, float64) { h := generic.NewHistogram("quantile-test", buckets) stats(w, regex, h) return h.Quantile(0.50), h.Quantile(0.90), h.Quantile(0.95), h.Quantile(0.99) } }
func TestHistogram(t *testing.T) { in := New(map[string]string{"foo": "alpha"}, influxdb.BatchPointsConfig{}, log.NewNopLogger()) re := regexp.MustCompile(`influx_histogram,foo=alpha bar="beta",value=([0-9\.]+) [0-9]+`) histogram := in.NewHistogram("influx_histogram").With("bar", "beta") quantiles := func() (float64, float64, float64, float64) { w := &bufWriter{} in.WriteTo(w) h := generic.NewHistogram("h", 50) matches := re.FindAllStringSubmatch(w.buf.String(), -1) for _, match := range matches { f, _ := strconv.ParseFloat(match[1], 64) h.Observe(f) } return h.Quantile(0.50), h.Quantile(0.90), h.Quantile(0.95), h.Quantile(0.99) } if err := teststat.TestHistogram(histogram, quantiles, 0.01); err != nil { t.Fatal(err) } }
func TestHistogram(t *testing.T) { const name = "ghi" // Circonus just emits bucketed counts. We'll dump them into a generic // histogram (losing some precision) and take statistics from there. Note // this does assume that the generic histogram computes statistics properly, // but we have another test for that :) re := regexp.MustCompile(`^H\[([0-9\.e\+]+)\]=([0-9]+)$`) // H[1.2e+03]=456 var p50, p90, p95, p99 float64 s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { var res map[string]struct { Values []string `json:"_value"` // reverse-engineered :\ } json.NewDecoder(r.Body).Decode(&res) h := generic.NewHistogram("dummy", len(res[name].Values)) // match tbe bucket counts for _, v := range res[name].Values { match := re.FindStringSubmatch(v) f, _ := strconv.ParseFloat(match[1], 64) n, _ := strconv.ParseInt(match[2], 10, 64) for i := int64(0); i < n; i++ { h.Observe(f) } } p50 = h.Quantile(0.50) p90 = h.Quantile(0.90) p95 = h.Quantile(0.95) p99 = h.Quantile(0.99) })) defer s.Close() m := newCirconusMetrics(s.URL) histogram := New(m).NewHistogram(name).With("label values", "not supported") quantiles := func() (float64, float64, float64, float64) { m.Flush(); return p50, p90, p95, p99 } // Circonus metrics, because they do their own bucketing, are less precise // than other systems. So, we bump the tolerance to 5 percent. if err := teststat.TestHistogram(histogram, quantiles, 0.05); err != nil { t.Fatal(err) } }
// NewHistogram returns a new usable Histogram metric. func NewHistogram(name string, buckets int) *Histogram { return &Histogram{generic.NewHistogram(name, buckets)} }