func TestPrintDistribution(t *testing.T) { var ( quantiles = []int{50, 90, 95, 99} h = expvar.NewHistogram("test_print_distribution", 0, 100, 3, quantiles...) seed = int64(555) mean = int64(5) stdev = int64(1) ) teststat.PopulateNormalHistogram(t, h, seed, mean, stdev) var buf bytes.Buffer metrics.PrintDistribution(&buf, h) t.Logf("\n%s\n", buf.String()) // Count the number of bar chart characters. // We should have ca. 100 in any distribution with a small-enough stdev. var n int for _, r := range buf.String() { if r == '#' { n++ } } if want, have, tol := 100, n, 5; int(math.Abs(float64(want-have))) > tol { t.Errorf("want %d, have %d (tolerance %d)", want, have, tol) } }
func TestHistogramQuantiles(t *testing.T) { metricName := "test_histogram" quantiles := []int{50, 90, 95, 99} h := expvar.NewHistogram(metricName, 0, 100, 3, quantiles...) const seed, mean, stdev int64 = 424242, 50, 10 teststat.PopulateNormalHistogram(t, h, seed, mean, stdev) teststat.AssertExpvarNormalHistogram(t, metricName, mean, stdev, quantiles) }
func TestHistogramQuantiles(t *testing.T) { var ( name = "test_histogram" quantiles = []int{50, 90, 95, 99} h = expvar.NewHistogram(name, 0, 100, 3, quantiles...).With(metrics.Field{Key: "ignored", Value: "field"}) ) const seed, mean, stdev int64 = 424242, 50, 10 teststat.PopulateNormalHistogram(t, h, seed, mean, stdev) teststat.AssertExpvarNormalHistogram(t, name, mean, stdev, quantiles) }
func TestPrometheusSummary(t *testing.T) { h := prometheus.NewSummary(stdprometheus.SummaryOpts{ Namespace: "test", Subsystem: "prometheus_summary_histogram", Name: "foobar", Help: "Qwerty asdf.", }, []string{}) const mean, stdev int64 = 50, 10 teststat.PopulateNormalHistogram(t, h, 34, mean, stdev) teststat.AssertPrometheusNormalSummary(t, "test_prometheus_summary_histogram_foobar", mean, stdev) }
func TestHistogram(t *testing.T) { log.SetOutput(ioutil.Discard) defer circonusgometrics.Reset() var ( name = "test_histogram" result []string mtx sync.Mutex onceDecode sync.Once ) s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { type postHistogram struct { Value []string `json:"_value"` } onceDecode.Do(func() { m := map[string]postHistogram{} json.NewDecoder(r.Body).Decode(&m) mtx.Lock() defer mtx.Unlock() result = m[name].Value }) })) defer s.Close() circonusgometrics.WithSubmissionUrl(s.URL) circonusgometrics.WithInterval(submissionInterval) h := NewHistogram(name) var ( seed = int64(123) mean = int64(500) stdev = int64(123) min = int64(0) max = 2 * mean ) teststat.PopulateNormalHistogram(t, h, seed, mean, stdev) onceStart.Do(func() { circonusgometrics.Start() }) if err := within(time.Second, func() bool { mtx.Lock() defer mtx.Unlock() return len(result) > 0 }); err != nil { t.Fatalf("error collecting results: %v", err) } teststat.AssertCirconusNormalHistogram(t, mean, stdev, min, max, result) }
func TestPrometheusHistogram(t *testing.T) { buckets := []float64{20, 40, 60, 80, 100} h := prometheus.NewHistogram(stdprometheus.HistogramOpts{ Namespace: "test", Subsystem: "prometheus_histogram_histogram", Name: "quux", Help: "Qwerty asdf.", Buckets: buckets, }, []string{}) const mean, stdev int64 = 50, 10 teststat.PopulateNormalHistogram(t, h, 34, mean, stdev) teststat.AssertPrometheusBucketedHistogram(t, "test_prometheus_histogram_histogram_quux_bucket", mean, stdev, buckets) }
func TestScaledHistogram(t *testing.T) { var ( quantiles = []int{50, 90, 99} scale = int64(10) metricName = "test_scaled_histogram" ) var h metrics.Histogram h = expvar.NewHistogram(metricName, 0, 1000, 3, quantiles...) h = metrics.NewScaledHistogram(h, scale) h = h.With(metrics.Field{Key: "a", Value: "b"}) const seed, mean, stdev = 333, 500, 100 // input values teststat.PopulateNormalHistogram(t, h, seed, mean, stdev) // will be scaled down assertExpvarNormalHistogram(t, metricName, mean/scale, stdev/scale, quantiles) }
func TestMultiHistogram(t *testing.T) { quantiles := []int{50, 90, 99} h := metrics.NewMultiHistogram( "multiomicron", expvar.NewHistogram("omicron", 0, 100, 3, quantiles...), prometheus.NewSummary(stdprometheus.SummaryOpts{ Namespace: "test", Subsystem: "multi_histogram", Name: "nu", Help: "Nu histogram.", }, []string{}), ) const seed, mean, stdev int64 = 123, 50, 10 teststat.PopulateNormalHistogram(t, h, seed, mean, stdev) assertExpvarNormalHistogram(t, "omicron", mean, stdev, quantiles) assertPrometheusNormalHistogram(t, `test_multi_histogram_nu`, mean, stdev) }
func TestHistogramQuantiles(t *testing.T) { prefix := "prefix." e := NewEmitter("", "", prefix, time.Second, log.NewNopLogger()) var ( name = "test_histogram_quantiles" quantiles = []int{50, 90, 95, 99} ) h, err := e.NewHistogram(name, 0, 100, 3, quantiles...) if err != nil { t.Fatalf("unable to create test histogram: %v", err) } h = h.With(metrics.Field{Key: "ignored", Value: "field"}) const seed, mean, stdev int64 = 424242, 50, 10 teststat.PopulateNormalHistogram(t, h, seed, mean, stdev) // flush the current metrics into a buffer to examine var b bytes.Buffer e.flush(&b) teststat.AssertGraphiteNormalHistogram(t, prefix, name, mean, stdev, quantiles, b.String()) }
func TestHistogram(t *testing.T) { // Prometheus reports histograms as a count of observations that fell into // each predefined bucket, with the bucket value representing a global upper // limit. That is, the count monotonically increases over the buckets. This // requires a different strategy to test. s := httptest.NewServer(stdprometheus.UninstrumentedHandler()) defer s.Close() scrape := func() string { resp, _ := http.Get(s.URL) buf, _ := ioutil.ReadAll(resp.Body) return string(buf) } namespace, subsystem, name := "test", "prometheus", "histogram" re := regexp.MustCompile(namespace + `_` + subsystem + `_` + name + `_bucket{x="1",le="([0-9]+|\+Inf)"} ([0-9\.]+)`) numStdev := 3 bucketMin := (teststat.Mean - (numStdev * teststat.Stdev)) bucketMax := (teststat.Mean + (numStdev * teststat.Stdev)) if bucketMin < 0 { bucketMin = 0 } bucketCount := 10 bucketDelta := (bucketMax - bucketMin) / bucketCount buckets := []float64{} for i := bucketMin; i <= bucketMax; i += bucketDelta { buckets = append(buckets, float64(i)) } histogram := NewHistogramFrom(stdprometheus.HistogramOpts{ Namespace: namespace, Subsystem: subsystem, Name: name, Help: "This is the help string for the histogram.", Buckets: buckets, }, []string{"x"}).With("x", "1") // Can't TestHistogram, because Prometheus Histograms don't dynamically // compute quantiles. Instead, they fill up buckets. So, let's populate the // histogram kind of manually. teststat.PopulateNormalHistogram(histogram, rand.Int()) // Then, we use ExpectedObservationsLessThan to validate. for _, line := range strings.Split(scrape(), "\n") { match := re.FindStringSubmatch(line) if match == nil { continue } bucket, _ := strconv.ParseInt(match[1], 10, 64) have, _ := strconv.ParseInt(match[2], 10, 64) want := teststat.ExpectedObservationsLessThan(bucket) if match[1] == "+Inf" { want = int64(teststat.Count) // special case } // Unfortunately, we observe experimentally that Prometheus is quite // imprecise at the extremes. I'm setting a very high tolerance for now. // It would be great to dig in and figure out whether that's a problem // with my Expected calculation, or in Prometheus. tolerance := 0.25 if delta := math.Abs(float64(want) - float64(have)); (delta / float64(want)) > tolerance { t.Errorf("Bucket %d: want %d, have %d (%.1f%%)", bucket, want, have, (100.0 * delta / float64(want))) } } }