*TL;DR: I'm unit-testing Prometheus metrics: server's requests count and 
latencies. The server is written in Go. Latencies are non-determinstic and 
there seems to be no good way to fake clock in Prometheus or Go. Does 
Prometheus provide a method similar to CollectAndCompare that lets 
validating a value (e.g. latency) against a range, not exact expectation? 
If not, would it be a good feature to add to Prometheus? Alternatively, is 
there a way to fake clock in Prometheus, so 
e.g. InstrumentHandlerDuration would propagate the pre-defined fake 
duration? Or, is there a good way to fake clock in Go?*

I'm measuring server's requests count (Counter) and latencies (Summary), 
and test both with unit tests. I observe the metrics via 
InstrumentHandlerCounter and InstrumentHandlerDuration respectively.

For testing requests count, I hardcode the expectation in a string 
constant, and use CollectAndCompare to perform an exact match validation:

// Make some test requests.
..

// Validate them
expectation := strings.NewReader(`
# HELP my_metric_requests_total My help.
# TYPE my_metric_requests_total counter
my_metric_requests_total{code="200"} 2
my_metric_requests_total{code="304"} 1
my_metric_requests_total{code="502"} 1
my_metric_requests_total{code="503"} 1
`)

this.Require().NoError(promtest.CollectAndCompare(myMetricRequestsTotal, 
expectation, "my_metric_requests_total"))

I couldn't find a way to do the same for latencies, because they are 
non-deterministic. So instead of the one-liner check above I dig into the 
internals of the gathered metrics:

// Make some test requests. 
hintPrefix := "My test."
...

// Validate them
type codeLabelPair string
type scenarioExpectedSampleCountMap map[codeLabelPair]uint64

expectedSampleCountMap := scenarioExpectedSampleCountMap{
`name:"code" value:"200" `: 3,
`name:"code" value:"304" `: 1,
`name:"code" value:"502" `: 2,
}

reg := prometheus.NewPedanticRegistry()
if err := reg.Register(promRequestsLatency); err != nil {
this.T().Errorf(hintPrefix+" - registering collector failed: %s", err)
}

actualMetricFamilyArr, err := reg.Gather()
if err != nil {
this.T().Errorf(hintPrefix+" - gathering metrics failed: %s", err)
}

assert.Equal(this.T(), 1, len(actualMetricFamilyArr),
hintPrefix+" expects exactly one metric family.")

assert.Equal(this.T(), "request_latencies_in_seconds", 
*actualMetricFamilyArr[0].Name,
hintPrefix+" expects the right metric name.")

assert.Equal(this.T(), len(expectedSampleCountMap), 
len(actualMetricFamilyArr[0].Metric),
hintPrefix+" expects the right amount of metrics collected and gathered.")

for _, actualMetric := range actualMetricFamilyArr[0].Metric {
// Expect the right sample count.
code := actualMetric.Label[0].String()
expectedSampleCount := expectedSampleCountMap[codeLabelPair(code)]
actualSampleCount := actualMetric.Summary.GetSampleCount()
assert.Equal(this.T(), expectedSampleCount, actualSampleCount, hintPrefix+" 
expects the right sample count for "+code)

// Test quantiles.
expectedQuantileKeys := []float64{0.5, 0.9, 0.99}

// Expect the right number of quantiles.
assert.Equal(this.T(), len(expectedQuantileKeys), 
len(actualMetric.Summary.Quantile), hintPrefix+" expects the right number 
of quantiles.")

// Expect the right quantiles.
// Expect positive quantile values, because latencies are non-zero.
// Don't check the exact values, because latencies are non-deterministic.
for i, quantile := range actualMetric.Summary.Quantile {
assert.Equal(this.T(), expectedQuantileKeys[i], quantile.GetQuantile(), 
hintPrefix+" expects the right quantile.")
assert.True(this.T(), quantile.GetValue() > .0, hintPrefix+" expects 
non-zero quantile value (latency).")
}
}

This seems to be more complex than it should be. Is there a one-liner way, 
similar to the CollectAndCompare call I'm making above to validate requests 
count? 

Alternatively, is there a way to fake clock in Prometheus, so e.g. 
InstrumentHandlerDuration would propagate the pre-defined fake duration? 
Or, is there a good way to fake clock in Go? There is an option that 
doesn't look safe enough:

https://www.reddit.com/r/golang/comments/30try1/monkey_patching_in_go/
https://news.ycombinator.com/item?id=22442170.

Thanks.

-- 
You received this message because you are subscribed to the Google Groups 
"Prometheus Developers" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/prometheus-developers/b440c496-cfb1-4b5f-8fb5-11ca47485a3cn%40googlegroups.com.

Reply via email to