github.com/kaituanwang/hyperledger@v2.0.1+incompatible/common/metrics/prometheus/provider_test.go (about)

     1  /*
     2  Copyright IBM Corp. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package prometheus_test
     8  
     9  import (
    10  	"fmt"
    11  	"io/ioutil"
    12  	"net/http"
    13  	"net/http/httptest"
    14  
    15  	commonmetrics "github.com/hyperledger/fabric/common/metrics"
    16  	"github.com/hyperledger/fabric/common/metrics/prometheus"
    17  	. "github.com/onsi/ginkgo"
    18  	. "github.com/onsi/gomega"
    19  	prom "github.com/prometheus/client_golang/prometheus"
    20  	"github.com/prometheus/client_golang/prometheus/promhttp"
    21  )
    22  
    23  var _ = Describe("Provider", func() {
    24  	var (
    25  		server *httptest.Server
    26  		client *http.Client
    27  		p      *prometheus.Provider
    28  	)
    29  
    30  	BeforeEach(func() {
    31  		// Note: These tests can't be run in parallel because go-kit uses
    32  		// the global registry to manage metrics. This is something to revisit
    33  		// in the future.
    34  		registry := prom.NewRegistry()
    35  		prom.DefaultRegisterer = registry
    36  		prom.DefaultGatherer = registry
    37  
    38  		server = httptest.NewServer(promhttp.HandlerFor(registry, promhttp.HandlerOpts{}))
    39  		client = server.Client()
    40  
    41  		p = &prometheus.Provider{}
    42  	})
    43  
    44  	AfterEach(func() {
    45  		server.Close()
    46  	})
    47  
    48  	It("implements metrics.Provider", func() {
    49  		var p commonmetrics.Provider = &prometheus.Provider{}
    50  		Expect(p).NotTo(BeNil())
    51  	})
    52  
    53  	Describe("NewCounter", func() {
    54  		var counterOpts commonmetrics.CounterOpts
    55  
    56  		BeforeEach(func() {
    57  			counterOpts = commonmetrics.CounterOpts{
    58  				Namespace:  "peer",
    59  				Subsystem:  "playground",
    60  				Name:       "counter_name",
    61  				Help:       "This is some help text for the counter",
    62  				LabelNames: []string{"alpha", "beta"},
    63  			}
    64  		})
    65  
    66  		It("creates counters that support labels", func() {
    67  			counter := p.NewCounter(counterOpts)
    68  			counter.With("alpha", "a", "beta", "b").Add(1)
    69  			counter.With("alpha", "aardvark", "beta", "b").Add(2)
    70  
    71  			resp, err := client.Get(fmt.Sprintf("http://%s/metrics", server.Listener.Addr().String()))
    72  			Expect(err).NotTo(HaveOccurred())
    73  			defer resp.Body.Close()
    74  
    75  			bytes, err := ioutil.ReadAll(resp.Body)
    76  			Expect(err).NotTo(HaveOccurred())
    77  			Expect(string(bytes)).To(ContainSubstring(`# HELP peer_playground_counter_name This is some help text for the counter`))
    78  			Expect(string(bytes)).To(ContainSubstring(`# TYPE peer_playground_counter_name counter`))
    79  			Expect(string(bytes)).To(ContainSubstring(`peer_playground_counter_name{alpha="a",beta="b"} 1`))
    80  			Expect(string(bytes)).To(ContainSubstring(`peer_playground_counter_name{alpha="aardvark",beta="b"} 2`))
    81  		})
    82  
    83  		Context("when the counter is defined without labels", func() {
    84  			BeforeEach(func() {
    85  				counterOpts.LabelNames = nil
    86  			})
    87  
    88  			It("With does not need to be called", func() {
    89  				counter := p.NewCounter(counterOpts)
    90  				counter.Add(1)
    91  
    92  				resp, err := client.Get(fmt.Sprintf("http://%s/metrics", server.Listener.Addr().String()))
    93  				Expect(err).NotTo(HaveOccurred())
    94  				defer resp.Body.Close()
    95  
    96  				bytes, err := ioutil.ReadAll(resp.Body)
    97  				Expect(err).NotTo(HaveOccurred())
    98  				Expect(string(bytes)).To(ContainSubstring(`peer_playground_counter_name 1`))
    99  			})
   100  		})
   101  	})
   102  
   103  	Describe("NewGauge", func() {
   104  		var gaugeOpts commonmetrics.GaugeOpts
   105  
   106  		BeforeEach(func() {
   107  			gaugeOpts = commonmetrics.GaugeOpts{
   108  				Namespace:  "peer",
   109  				Subsystem:  "playground",
   110  				Name:       "gauge_name",
   111  				Help:       "This is some help text for the gauge",
   112  				LabelNames: []string{"alpha", "beta"},
   113  			}
   114  		})
   115  
   116  		It("creates gauges that support labels", func() {
   117  			gauge := p.NewGauge(gaugeOpts)
   118  			gauge.With("alpha", "a", "beta", "b").Add(1)
   119  			gauge.With("alpha", "a", "beta", "b").Add(1)
   120  			gauge.With("alpha", "aardvark", "beta", "b").Add(1)
   121  			gauge.With("alpha", "aardvark", "beta", "bob").Set(99)
   122  
   123  			resp, err := client.Get(fmt.Sprintf("http://%s/metrics", server.Listener.Addr().String()))
   124  			Expect(err).NotTo(HaveOccurred())
   125  			defer resp.Body.Close()
   126  
   127  			bytes, err := ioutil.ReadAll(resp.Body)
   128  			Expect(err).NotTo(HaveOccurred())
   129  			Expect(string(bytes)).To(ContainSubstring(`# HELP peer_playground_gauge_name This is some help text for the gauge`))
   130  			Expect(string(bytes)).To(ContainSubstring(`# TYPE peer_playground_gauge_name gauge`))
   131  			Expect(string(bytes)).To(ContainSubstring(`peer_playground_gauge_name{alpha="a",beta="b"} 2`))
   132  			Expect(string(bytes)).To(ContainSubstring(`peer_playground_gauge_name{alpha="aardvark",beta="b"} 1`))
   133  			Expect(string(bytes)).To(ContainSubstring(`peer_playground_gauge_name{alpha="aardvark",beta="bob"} 99`))
   134  		})
   135  	})
   136  
   137  	Describe("NewHistogram", func() {
   138  		var histogramOpts commonmetrics.HistogramOpts
   139  
   140  		BeforeEach(func() {
   141  			histogramOpts = commonmetrics.HistogramOpts{
   142  				Namespace:  "peer",
   143  				Subsystem:  "playground",
   144  				Name:       "histogram_name",
   145  				Help:       "This is some help text for the gauge",
   146  				LabelNames: []string{"alpha", "beta"},
   147  			}
   148  		})
   149  
   150  		It("creates histogram that support labels", func() {
   151  			histogram := p.NewHistogram(histogramOpts)
   152  			for _, limit := range prom.DefBuckets {
   153  				histogram.With("alpha", "a", "beta", "b").Observe(limit)
   154  			}
   155  			histogram.With("alpha", "a", "beta", "b").Observe(prom.DefBuckets[len(prom.DefBuckets)-1] + 1)
   156  
   157  			resp, err := client.Get(fmt.Sprintf("http://%s/metrics", server.Listener.Addr().String()))
   158  			Expect(err).NotTo(HaveOccurred())
   159  			defer resp.Body.Close()
   160  
   161  			bytes, err := ioutil.ReadAll(resp.Body)
   162  			Expect(err).NotTo(HaveOccurred())
   163  			Expect(string(bytes)).To(ContainSubstring(`# HELP peer_playground_histogram_name This is some help text for the gauge`))
   164  			Expect(string(bytes)).To(ContainSubstring(`# TYPE peer_playground_histogram_name histogram`))
   165  			Expect(string(bytes)).To(ContainSubstring(`peer_playground_histogram_name_bucket{alpha="a",beta="b",le="0.005"} 1`))
   166  			Expect(string(bytes)).To(ContainSubstring(`peer_playground_histogram_name_bucket{alpha="a",beta="b",le="0.01"} 2`))
   167  			Expect(string(bytes)).To(ContainSubstring(`peer_playground_histogram_name_bucket{alpha="a",beta="b",le="0.025"} 3`))
   168  			Expect(string(bytes)).To(ContainSubstring(`peer_playground_histogram_name_bucket{alpha="a",beta="b",le="0.05"} 4`))
   169  			Expect(string(bytes)).To(ContainSubstring(`peer_playground_histogram_name_bucket{alpha="a",beta="b",le="0.1"} 5`))
   170  			Expect(string(bytes)).To(ContainSubstring(`peer_playground_histogram_name_bucket{alpha="a",beta="b",le="0.25"} 6`))
   171  			Expect(string(bytes)).To(ContainSubstring(`peer_playground_histogram_name_bucket{alpha="a",beta="b",le="0.5"} 7`))
   172  			Expect(string(bytes)).To(ContainSubstring(`peer_playground_histogram_name_bucket{alpha="a",beta="b",le="1"} 8`))
   173  			Expect(string(bytes)).To(ContainSubstring(`peer_playground_histogram_name_bucket{alpha="a",beta="b",le="2.5"} 9`))
   174  			Expect(string(bytes)).To(ContainSubstring(`peer_playground_histogram_name_bucket{alpha="a",beta="b",le="5"} 10`))
   175  			Expect(string(bytes)).To(ContainSubstring(`peer_playground_histogram_name_bucket{alpha="a",beta="b",le="10"} 11`))
   176  			Expect(string(bytes)).To(ContainSubstring(`peer_playground_histogram_name_bucket{alpha="a",beta="b",le="+Inf"} 12`))
   177  			Expect(string(bytes)).To(ContainSubstring(`peer_playground_histogram_name_sum{alpha="a",beta="b"} `))
   178  			Expect(string(bytes)).To(ContainSubstring(`peer_playground_histogram_name_count{alpha="a",beta="b"} 12`))
   179  		})
   180  
   181  		It("creates histogram with buckets that support labels", func() {
   182  			histogramOpts.Buckets = []float64{1, 5}
   183  			histogram := p.NewHistogram(histogramOpts)
   184  
   185  			histogram.With("alpha", "a", "beta", "b").Observe(0.5)
   186  			histogram.With("alpha", "a", "beta", "b").Observe(4.5)
   187  
   188  			resp, err := client.Get(fmt.Sprintf("http://%s/metrics", server.Listener.Addr().String()))
   189  			Expect(err).NotTo(HaveOccurred())
   190  			defer resp.Body.Close()
   191  
   192  			bytes, err := ioutil.ReadAll(resp.Body)
   193  			Expect(err).NotTo(HaveOccurred())
   194  			Expect(string(bytes)).To(ContainSubstring(`# HELP peer_playground_histogram_name This is some help text for the gauge`))
   195  			Expect(string(bytes)).To(ContainSubstring(`# TYPE peer_playground_histogram_name histogram`))
   196  			Expect(string(bytes)).To(ContainSubstring(`peer_playground_histogram_name_bucket{alpha="a",beta="b",le="1"} 1`))
   197  			Expect(string(bytes)).To(ContainSubstring(`peer_playground_histogram_name_bucket{alpha="a",beta="b",le="5"} 2`))
   198  			Expect(string(bytes)).To(ContainSubstring(`peer_playground_histogram_name_sum{alpha="a",beta="b"} 5`))
   199  			Expect(string(bytes)).To(ContainSubstring(`peer_playground_histogram_name_count{alpha="a",beta="b"} 2`))
   200  		})
   201  	})
   202  
   203  	// This helps ensure the label cardinality behavior matches what was implemented
   204  	// for statsd. If these tests fail, correspending updates will be needed in the label
   205  	// processing used for statsd.
   206  	Describe("edge case behavior", func() {
   207  		var counterOpts commonmetrics.CounterOpts
   208  
   209  		BeforeEach(func() {
   210  			counterOpts = commonmetrics.CounterOpts{
   211  				Namespace:  "peer",
   212  				Subsystem:  "playground",
   213  				Name:       "counter_name",
   214  				Help:       "This is some help text for the counter",
   215  				LabelNames: []string{"alpha", "beta"},
   216  			}
   217  		})
   218  		Context("when With is called without a label value", func() {
   219  			It("uses unknown for the missing value", func() {
   220  				counter := p.NewCounter(counterOpts)
   221  				counter.With("alpha", "a", "beta").Add(1)
   222  				resp, err := client.Get(fmt.Sprintf("http://%s/metrics", server.Listener.Addr().String()))
   223  				Expect(err).NotTo(HaveOccurred())
   224  				defer resp.Body.Close()
   225  
   226  				bytes, err := ioutil.ReadAll(resp.Body)
   227  				Expect(err).NotTo(HaveOccurred())
   228  				Expect(string(bytes)).To(ContainSubstring(`# HELP peer_playground_counter_name This is some help text for the counter`))
   229  				Expect(string(bytes)).To(ContainSubstring(`# TYPE peer_playground_counter_name counter`))
   230  				Expect(string(bytes)).To(ContainSubstring(`peer_playground_counter_name{alpha="a",beta="unknown"} 1`))
   231  			})
   232  		})
   233  
   234  		Context("when With is called with an extra label", func() {
   235  			It("panics", func() {
   236  				counter := p.NewCounter(counterOpts)
   237  				panicMessage := func() (panicMessage interface{}) {
   238  					defer func() { panicMessage = recover() }()
   239  					counter.With("alpha", "a", "beta", "b", "charlie", "c").Add(1)
   240  					return
   241  				}()
   242  				Expect(panicMessage).To(MatchError(MatchRegexp(`inconsistent label cardinality: expected 2 label values but got 3 in prometheus.Labels\{.*\}`)))
   243  			})
   244  		})
   245  
   246  		Context("when label values are not provided", func() {
   247  			It("it panics with a cardinaility message", func() {
   248  				counter := p.NewCounter(counterOpts)
   249  				panicMessage := func() (panicMessage interface{}) {
   250  					defer func() { panicMessage = recover() }()
   251  					counter.Add(1)
   252  					return
   253  				}()
   254  				Expect(panicMessage).To(MatchError(`inconsistent label cardinality: expected 2 label values but got 0 in prometheus.Labels{}`))
   255  			})
   256  		})
   257  	})
   258  })