go_study/fabric-main/common/metrics/prometheus/provider_test.go

259 lines
10 KiB
Go

/*
Copyright IBM Corp. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
package prometheus_test
import (
"fmt"
"io/ioutil"
"net/http"
"net/http/httptest"
commonmetrics "github.com/hyperledger/fabric/common/metrics"
"github.com/hyperledger/fabric/common/metrics/prometheus"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
prom "github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"
)
var _ = Describe("Provider", func() {
var (
server *httptest.Server
client *http.Client
p *prometheus.Provider
)
BeforeEach(func() {
// Note: These tests can't be run in parallel because go-kit uses
// the global registry to manage metrics. This is something to revisit
// in the future.
registry := prom.NewRegistry()
prom.DefaultRegisterer = registry
prom.DefaultGatherer = registry
server = httptest.NewServer(promhttp.HandlerFor(registry, promhttp.HandlerOpts{}))
client = server.Client()
p = &prometheus.Provider{}
})
AfterEach(func() {
server.Close()
})
It("implements metrics.Provider", func() {
var p commonmetrics.Provider = &prometheus.Provider{}
Expect(p).NotTo(BeNil())
})
Describe("NewCounter", func() {
var counterOpts commonmetrics.CounterOpts
BeforeEach(func() {
counterOpts = commonmetrics.CounterOpts{
Namespace: "peer",
Subsystem: "playground",
Name: "counter_name",
Help: "This is some help text for the counter",
LabelNames: []string{"alpha", "beta"},
}
})
It("creates counters that support labels", func() {
counter := p.NewCounter(counterOpts)
counter.With("alpha", "a", "beta", "b").Add(1)
counter.With("alpha", "aardvark", "beta", "b").Add(2)
resp, err := client.Get(fmt.Sprintf("http://%s/metrics", server.Listener.Addr().String()))
Expect(err).NotTo(HaveOccurred())
defer resp.Body.Close()
bytes, err := ioutil.ReadAll(resp.Body)
Expect(err).NotTo(HaveOccurred())
Expect(string(bytes)).To(ContainSubstring(`# HELP peer_playground_counter_name This is some help text for the counter`))
Expect(string(bytes)).To(ContainSubstring(`# TYPE peer_playground_counter_name counter`))
Expect(string(bytes)).To(ContainSubstring(`peer_playground_counter_name{alpha="a",beta="b"} 1`))
Expect(string(bytes)).To(ContainSubstring(`peer_playground_counter_name{alpha="aardvark",beta="b"} 2`))
})
Context("when the counter is defined without labels", func() {
BeforeEach(func() {
counterOpts.LabelNames = nil
})
It("With does not need to be called", func() {
counter := p.NewCounter(counterOpts)
counter.Add(1)
resp, err := client.Get(fmt.Sprintf("http://%s/metrics", server.Listener.Addr().String()))
Expect(err).NotTo(HaveOccurred())
defer resp.Body.Close()
bytes, err := ioutil.ReadAll(resp.Body)
Expect(err).NotTo(HaveOccurred())
Expect(string(bytes)).To(ContainSubstring(`peer_playground_counter_name 1`))
})
})
})
Describe("NewGauge", func() {
var gaugeOpts commonmetrics.GaugeOpts
BeforeEach(func() {
gaugeOpts = commonmetrics.GaugeOpts{
Namespace: "peer",
Subsystem: "playground",
Name: "gauge_name",
Help: "This is some help text for the gauge",
LabelNames: []string{"alpha", "beta"},
}
})
It("creates gauges that support labels", func() {
gauge := p.NewGauge(gaugeOpts)
gauge.With("alpha", "a", "beta", "b").Add(1)
gauge.With("alpha", "a", "beta", "b").Add(1)
gauge.With("alpha", "aardvark", "beta", "b").Add(1)
gauge.With("alpha", "aardvark", "beta", "bob").Set(99)
resp, err := client.Get(fmt.Sprintf("http://%s/metrics", server.Listener.Addr().String()))
Expect(err).NotTo(HaveOccurred())
defer resp.Body.Close()
bytes, err := ioutil.ReadAll(resp.Body)
Expect(err).NotTo(HaveOccurred())
Expect(string(bytes)).To(ContainSubstring(`# HELP peer_playground_gauge_name This is some help text for the gauge`))
Expect(string(bytes)).To(ContainSubstring(`# TYPE peer_playground_gauge_name gauge`))
Expect(string(bytes)).To(ContainSubstring(`peer_playground_gauge_name{alpha="a",beta="b"} 2`))
Expect(string(bytes)).To(ContainSubstring(`peer_playground_gauge_name{alpha="aardvark",beta="b"} 1`))
Expect(string(bytes)).To(ContainSubstring(`peer_playground_gauge_name{alpha="aardvark",beta="bob"} 99`))
})
})
Describe("NewHistogram", func() {
var histogramOpts commonmetrics.HistogramOpts
BeforeEach(func() {
histogramOpts = commonmetrics.HistogramOpts{
Namespace: "peer",
Subsystem: "playground",
Name: "histogram_name",
Help: "This is some help text for the gauge",
LabelNames: []string{"alpha", "beta"},
}
})
It("creates histogram that support labels", func() {
histogram := p.NewHistogram(histogramOpts)
for _, limit := range prom.DefBuckets {
histogram.With("alpha", "a", "beta", "b").Observe(limit)
}
histogram.With("alpha", "a", "beta", "b").Observe(prom.DefBuckets[len(prom.DefBuckets)-1] + 1)
resp, err := client.Get(fmt.Sprintf("http://%s/metrics", server.Listener.Addr().String()))
Expect(err).NotTo(HaveOccurred())
defer resp.Body.Close()
bytes, err := ioutil.ReadAll(resp.Body)
Expect(err).NotTo(HaveOccurred())
Expect(string(bytes)).To(ContainSubstring(`# HELP peer_playground_histogram_name This is some help text for the gauge`))
Expect(string(bytes)).To(ContainSubstring(`# TYPE peer_playground_histogram_name histogram`))
Expect(string(bytes)).To(ContainSubstring(`peer_playground_histogram_name_bucket{alpha="a",beta="b",le="0.005"} 1`))
Expect(string(bytes)).To(ContainSubstring(`peer_playground_histogram_name_bucket{alpha="a",beta="b",le="0.01"} 2`))
Expect(string(bytes)).To(ContainSubstring(`peer_playground_histogram_name_bucket{alpha="a",beta="b",le="0.025"} 3`))
Expect(string(bytes)).To(ContainSubstring(`peer_playground_histogram_name_bucket{alpha="a",beta="b",le="0.05"} 4`))
Expect(string(bytes)).To(ContainSubstring(`peer_playground_histogram_name_bucket{alpha="a",beta="b",le="0.1"} 5`))
Expect(string(bytes)).To(ContainSubstring(`peer_playground_histogram_name_bucket{alpha="a",beta="b",le="0.25"} 6`))
Expect(string(bytes)).To(ContainSubstring(`peer_playground_histogram_name_bucket{alpha="a",beta="b",le="0.5"} 7`))
Expect(string(bytes)).To(ContainSubstring(`peer_playground_histogram_name_bucket{alpha="a",beta="b",le="1"} 8`))
Expect(string(bytes)).To(ContainSubstring(`peer_playground_histogram_name_bucket{alpha="a",beta="b",le="2.5"} 9`))
Expect(string(bytes)).To(ContainSubstring(`peer_playground_histogram_name_bucket{alpha="a",beta="b",le="5"} 10`))
Expect(string(bytes)).To(ContainSubstring(`peer_playground_histogram_name_bucket{alpha="a",beta="b",le="10"} 11`))
Expect(string(bytes)).To(ContainSubstring(`peer_playground_histogram_name_bucket{alpha="a",beta="b",le="+Inf"} 12`))
Expect(string(bytes)).To(ContainSubstring(`peer_playground_histogram_name_sum{alpha="a",beta="b"} `))
Expect(string(bytes)).To(ContainSubstring(`peer_playground_histogram_name_count{alpha="a",beta="b"} 12`))
})
It("creates histogram with buckets that support labels", func() {
histogramOpts.Buckets = []float64{1, 5}
histogram := p.NewHistogram(histogramOpts)
histogram.With("alpha", "a", "beta", "b").Observe(0.5)
histogram.With("alpha", "a", "beta", "b").Observe(4.5)
resp, err := client.Get(fmt.Sprintf("http://%s/metrics", server.Listener.Addr().String()))
Expect(err).NotTo(HaveOccurred())
defer resp.Body.Close()
bytes, err := ioutil.ReadAll(resp.Body)
Expect(err).NotTo(HaveOccurred())
Expect(string(bytes)).To(ContainSubstring(`# HELP peer_playground_histogram_name This is some help text for the gauge`))
Expect(string(bytes)).To(ContainSubstring(`# TYPE peer_playground_histogram_name histogram`))
Expect(string(bytes)).To(ContainSubstring(`peer_playground_histogram_name_bucket{alpha="a",beta="b",le="1"} 1`))
Expect(string(bytes)).To(ContainSubstring(`peer_playground_histogram_name_bucket{alpha="a",beta="b",le="5"} 2`))
Expect(string(bytes)).To(ContainSubstring(`peer_playground_histogram_name_sum{alpha="a",beta="b"} 5`))
Expect(string(bytes)).To(ContainSubstring(`peer_playground_histogram_name_count{alpha="a",beta="b"} 2`))
})
})
// This helps ensure the label cardinality behavior matches what was implemented
// for statsd. If these tests fail, correspending updates will be needed in the label
// processing used for statsd.
Describe("edge case behavior", func() {
var counterOpts commonmetrics.CounterOpts
BeforeEach(func() {
counterOpts = commonmetrics.CounterOpts{
Namespace: "peer",
Subsystem: "playground",
Name: "counter_name",
Help: "This is some help text for the counter",
LabelNames: []string{"alpha", "beta"},
}
})
Context("when With is called without a label value", func() {
It("uses unknown for the missing value", func() {
counter := p.NewCounter(counterOpts)
counter.With("alpha", "a", "beta").Add(1)
resp, err := client.Get(fmt.Sprintf("http://%s/metrics", server.Listener.Addr().String()))
Expect(err).NotTo(HaveOccurred())
defer resp.Body.Close()
bytes, err := ioutil.ReadAll(resp.Body)
Expect(err).NotTo(HaveOccurred())
Expect(string(bytes)).To(ContainSubstring(`# HELP peer_playground_counter_name This is some help text for the counter`))
Expect(string(bytes)).To(ContainSubstring(`# TYPE peer_playground_counter_name counter`))
Expect(string(bytes)).To(ContainSubstring(`peer_playground_counter_name{alpha="a",beta="unknown"} 1`))
})
})
Context("when With is called with an extra label", func() {
It("panics", func() {
counter := p.NewCounter(counterOpts)
panicMessage := func() (panicMessage interface{}) {
defer func() { panicMessage = recover() }()
counter.With("alpha", "a", "beta", "b", "charlie", "c").Add(1)
return
}()
Expect(panicMessage).To(MatchError(MatchRegexp(`inconsistent label cardinality: expected 2 label values but got 3 in prometheus.Labels\{.*\}`)))
})
})
Context("when label values are not provided", func() {
It("it panics with a cardinaility message", func() {
counter := p.NewCounter(counterOpts)
panicMessage := func() (panicMessage interface{}) {
defer func() { panicMessage = recover() }()
counter.Add(1)
return
}()
Expect(panicMessage).To(MatchError(`inconsistent label cardinality: expected 2 label values but got 0 in prometheus.Labels{}`))
})
})
})
})