vitess.io/vitess@v0.16.2/go/stats/prometheusbackend/prometheusbackend_test.go (about)

     1  /*
     2  Copyright 2019 The Vitess Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package prometheusbackend
    18  
    19  import (
    20  	"fmt"
    21  	"net/http"
    22  	"net/http/httptest"
    23  	"os"
    24  	"strings"
    25  	"testing"
    26  	"time"
    27  
    28  	"vitess.io/vitess/go/stats"
    29  
    30  	"github.com/prometheus/client_golang/prometheus/promhttp"
    31  )
    32  
    33  const namespace = "namespace"
    34  
    35  func TestPrometheusCounter(t *testing.T) {
    36  	name := "blah"
    37  	c := stats.NewCounter(name, "blah")
    38  	c.Add(1)
    39  	checkHandlerForMetrics(t, name, 1)
    40  	//TODO: ban this? And for other counter types too?
    41  	// c.Add(-1)
    42  	c.Reset()
    43  	checkHandlerForMetrics(t, name, 0)
    44  }
    45  
    46  func TestPrometheusGauge(t *testing.T) {
    47  	name := "blah_gauge"
    48  	c := stats.NewGauge(name, "help")
    49  	c.Add(1)
    50  	checkHandlerForMetrics(t, name, 1)
    51  	c.Add(-1)
    52  	checkHandlerForMetrics(t, name, 0)
    53  	c.Set(-5)
    54  	checkHandlerForMetrics(t, name, -5)
    55  	c.Reset()
    56  	checkHandlerForMetrics(t, name, 0)
    57  }
    58  
    59  func TestPrometheusGaugeFloat64(t *testing.T) {
    60  	name := "blah_gauge_f64"
    61  	c := stats.NewGaugeFloat64(name, "help")
    62  	c.Set(3.14)
    63  	checkHandlerForMetrics(t, name, 3)
    64  	c.Reset()
    65  	checkHandlerForMetrics(t, name, 0)
    66  }
    67  
    68  func TestPrometheusCounterFunc(t *testing.T) {
    69  	name := "blah_counterfunc"
    70  	stats.NewCounterFunc(name, "help", func() int64 {
    71  		return 2
    72  	})
    73  
    74  	checkHandlerForMetrics(t, name, 2)
    75  }
    76  
    77  func TestPrometheusGaugeFunc(t *testing.T) {
    78  	name := "blah_gaugefunc"
    79  
    80  	stats.NewGaugeFunc(name, "help", func() int64 {
    81  		return -3
    82  	})
    83  
    84  	checkHandlerForMetrics(t, name, -3)
    85  }
    86  
    87  func TestPrometheusFloatFunc(t *testing.T) {
    88  	name := "blah_floatfunc"
    89  
    90  	stats.Publish(name, stats.FloatFunc(func() float64 { return -4 }))
    91  	checkHandlerForMetrics(t, name, -4)
    92  }
    93  
    94  func TestPrometheusCounterDuration(t *testing.T) {
    95  	name := "blah_counterduration"
    96  
    97  	d := stats.NewCounterDuration(name, "help")
    98  	d.Add(1 * time.Second)
    99  
   100  	checkHandlerForMetrics(t, name, 1)
   101  }
   102  
   103  func TestPrometheusCounterDurationFunc(t *testing.T) {
   104  	name := "blah_counterdurationfunc"
   105  
   106  	stats.NewCounterDurationFunc(name, "help", func() time.Duration { return 1 * time.Second })
   107  
   108  	checkHandlerForMetrics(t, name, 1)
   109  }
   110  
   111  func TestPrometheusGaugeDuration(t *testing.T) {
   112  	name := "blah_gaugeduration"
   113  
   114  	d := stats.NewGaugeDuration(name, "help")
   115  	d.Set(1 * time.Second)
   116  
   117  	checkHandlerForMetrics(t, name, 1)
   118  }
   119  
   120  func TestPrometheusGaugeDurationFunc(t *testing.T) {
   121  	name := "blah_gaugedurationfunc"
   122  
   123  	stats.NewGaugeDurationFunc(name, "help", func() time.Duration { return 1 * time.Second })
   124  
   125  	checkHandlerForMetrics(t, name, 1)
   126  }
   127  
   128  func checkHandlerForMetrics(t *testing.T, metric string, value int) {
   129  	response := testMetricsHandler(t)
   130  
   131  	expected := fmt.Sprintf("%s_%s %d", namespace, metric, value)
   132  
   133  	if !strings.Contains(response.Body.String(), expected) {
   134  		t.Fatalf("Expected %s got %s", expected, response.Body.String())
   135  	}
   136  }
   137  
   138  func TestPrometheusCountersWithSingleLabel(t *testing.T) {
   139  	name := "blah_counterswithsinglelabel"
   140  	c := stats.NewCountersWithSingleLabel(name, "help", "label", "tag1", "tag2")
   141  	c.Add("tag1", 1)
   142  	checkHandlerForMetricWithSingleLabel(t, name, "label", "tag1", 1)
   143  	checkHandlerForMetricWithSingleLabel(t, name, "label", "tag2", 0)
   144  	c.Add("tag2", 41)
   145  	checkHandlerForMetricWithSingleLabel(t, name, "label", "tag1", 1)
   146  	checkHandlerForMetricWithSingleLabel(t, name, "label", "tag2", 41)
   147  	c.Reset("tag2")
   148  	checkHandlerForMetricWithSingleLabel(t, name, "label", "tag1", 1)
   149  	checkHandlerForMetricWithSingleLabel(t, name, "label", "tag2", 0)
   150  }
   151  
   152  func TestPrometheusGaugesWithSingleLabel(t *testing.T) {
   153  	name := "blah_gaugeswithsinglelabel"
   154  	c := stats.NewGaugesWithSingleLabel(name, "help", "label", "tag1", "tag2")
   155  	c.Add("tag1", 1)
   156  	checkHandlerForMetricWithSingleLabel(t, name, "label", "tag1", 1)
   157  
   158  	c.Add("tag2", 1)
   159  	checkHandlerForMetricWithSingleLabel(t, name, "label", "tag2", 1)
   160  
   161  	c.Set("tag1", -1)
   162  	checkHandlerForMetricWithSingleLabel(t, name, "label", "tag1", -1)
   163  
   164  	c.Reset("tag2")
   165  	checkHandlerForMetricWithSingleLabel(t, name, "label", "tag1", -1)
   166  	checkHandlerForMetricWithSingleLabel(t, name, "label", "tag2", 0)
   167  }
   168  
   169  func checkHandlerForMetricWithSingleLabel(t *testing.T, metric, label, tag string, value int) {
   170  	response := testMetricsHandler(t)
   171  
   172  	expected := fmt.Sprintf("%s_%s{%s=\"%s\"} %d", namespace, metric, label, tag, value)
   173  
   174  	if !strings.Contains(response.Body.String(), expected) {
   175  		t.Fatalf("Expected %s got %s", expected, response.Body.String())
   176  	}
   177  }
   178  
   179  func TestPrometheusCountersWithMultiLabels(t *testing.T) {
   180  	name := "blah_counterswithmultilabels"
   181  	labels := []string{"label1", "label2"}
   182  	labelValues := []string{"foo", "bar"}
   183  	c := stats.NewCountersWithMultiLabels(name, "help", labels)
   184  	c.Add(labelValues, 1)
   185  	checkHandlerForMetricWithMultiLabels(t, name, labels, labelValues, 1)
   186  	labelValues2 := []string{"baz", "bazbar"}
   187  	c.Add(labelValues2, 1)
   188  	checkHandlerForMetricWithMultiLabels(t, name, labels, labelValues, 1)
   189  	checkHandlerForMetricWithMultiLabels(t, name, labels, labelValues2, 1)
   190  	c.Reset(labelValues)
   191  	checkHandlerForMetricWithMultiLabels(t, name, labels, labelValues, 0)
   192  	checkHandlerForMetricWithMultiLabels(t, name, labels, labelValues2, 1)
   193  }
   194  
   195  func TestPrometheusGaugesWithMultiLabels(t *testing.T) {
   196  	name := "blah_gaugeswithmultilabels"
   197  	labels := []string{"label1", "label2"}
   198  	labelValues := []string{"foo", "bar"}
   199  	c := stats.NewGaugesWithMultiLabels(name, "help", labels)
   200  	c.Add(labelValues, 1)
   201  	checkHandlerForMetricWithMultiLabels(t, name, labels, labelValues, 1)
   202  
   203  	c.Set(labelValues, -1)
   204  	checkHandlerForMetricWithMultiLabels(t, name, labels, labelValues, -1)
   205  
   206  	labelValues2 := []string{"baz", "bazbar"}
   207  	c.Add(labelValues2, 1)
   208  	checkHandlerForMetricWithMultiLabels(t, name, labels, labelValues, -1)
   209  	checkHandlerForMetricWithMultiLabels(t, name, labels, labelValues2, 1)
   210  
   211  	c.Reset(labelValues)
   212  	checkHandlerForMetricWithMultiLabels(t, name, labels, labelValues, 0)
   213  	checkHandlerForMetricWithMultiLabels(t, name, labels, labelValues2, 1)
   214  }
   215  
   216  func TestPrometheusCountersWithMultiLabels_AddPanic(t *testing.T) {
   217  	defer func() {
   218  		if r := recover(); r == nil {
   219  			t.Errorf("The code did not panic when adding to inequal label lengths")
   220  		}
   221  	}()
   222  
   223  	name := "blah_counterswithmultilabels_inequallength"
   224  	c := stats.NewCountersWithMultiLabels(name, "help", []string{"label1", "label2"})
   225  	c.Add([]string{"label1"}, 1)
   226  }
   227  
   228  func TestPrometheusCountersFuncWithMultiLabels(t *testing.T) {
   229  	name := "blah_countersfuncwithmultilabels"
   230  	labels := []string{"label1", "label2"}
   231  
   232  	stats.NewCountersFuncWithMultiLabels(name, "help", labels, func() map[string]int64 {
   233  		m := make(map[string]int64)
   234  		m["foo.bar"] = 1
   235  		m["bar.baz"] = 1
   236  		return m
   237  	})
   238  
   239  	checkHandlerForMetricWithMultiLabels(t, name, labels, []string{"foo", "bar"}, 1)
   240  	checkHandlerForMetricWithMultiLabels(t, name, labels, []string{"bar", "baz"}, 1)
   241  }
   242  
   243  func checkHandlerForMetricWithMultiLabels(t *testing.T, metric string, labels []string, labelValues []string, value int64) {
   244  	response := testMetricsHandler(t)
   245  
   246  	expected := fmt.Sprintf("%s_%s{%s=\"%s\",%s=\"%s\"} %d", namespace, metric, labels[0], labelValues[0], labels[1], labelValues[1], value)
   247  
   248  	if !strings.Contains(response.Body.String(), expected) {
   249  		t.Fatalf("Expected %s got %s", expected, response.Body.String())
   250  	}
   251  }
   252  
   253  func TestPrometheusTimings(t *testing.T) {
   254  	name := "blah_timings"
   255  	cats := []string{"cat1", "cat2"}
   256  	timing := stats.NewTimings(name, "help", "category", cats...)
   257  	timing.Add("cat1", time.Duration(30*time.Millisecond))
   258  	timing.Add("cat1", time.Duration(200*time.Millisecond))
   259  	timing.Add("cat1", time.Duration(1*time.Second))
   260  
   261  	response := testMetricsHandler(t)
   262  	var s []string
   263  
   264  	s = append(s, fmt.Sprintf("%s_%s_bucket{category=\"%s\",le=\"0.0005\"} %d", namespace, name, cats[0], 0))
   265  	s = append(s, fmt.Sprintf("%s_%s_bucket{category=\"%s\",le=\"0.001\"} %d", namespace, name, cats[0], 0))
   266  	s = append(s, fmt.Sprintf("%s_%s_bucket{category=\"%s\",le=\"0.005\"} %d", namespace, name, cats[0], 0))
   267  	s = append(s, fmt.Sprintf("%s_%s_bucket{category=\"%s\",le=\"0.01\"} %d", namespace, name, cats[0], 0))
   268  	s = append(s, fmt.Sprintf("%s_%s_bucket{category=\"%s\",le=\"0.05\"} %d", namespace, name, cats[0], 1))
   269  	s = append(s, fmt.Sprintf("%s_%s_bucket{category=\"%s\",le=\"0.1\"} %d", namespace, name, cats[0], 1))
   270  	s = append(s, fmt.Sprintf("%s_%s_bucket{category=\"%s\",le=\"0.5\"} %d", namespace, name, cats[0], 2))
   271  	s = append(s, fmt.Sprintf("%s_%s_bucket{category=\"%s\",le=\"1\"} %d", namespace, name, cats[0], 3))
   272  	s = append(s, fmt.Sprintf("%s_%s_bucket{category=\"%s\",le=\"5\"} %d", namespace, name, cats[0], 3))
   273  	s = append(s, fmt.Sprintf("%s_%s_bucket{category=\"%s\",le=\"10\"} %d", namespace, name, cats[0], 3))
   274  	s = append(s, fmt.Sprintf("%s_%s_bucket{category=\"%s\",le=\"+Inf\"} %d", namespace, name, cats[0], 3))
   275  	s = append(s, fmt.Sprintf("%s_%s_sum{category=\"%s\"} %s", namespace, name, cats[0], "1.23"))
   276  	s = append(s, fmt.Sprintf("%s_%s_count{category=\"%s\"} %d", namespace, name, cats[0], 3))
   277  
   278  	for _, line := range s {
   279  		if !strings.Contains(response.Body.String(), line) {
   280  			t.Fatalf("Expected result to contain %s, got %s", line, response.Body.String())
   281  		}
   282  	}
   283  }
   284  
   285  func TestPrometheusMultiTimings(t *testing.T) {
   286  	name := "blah_multitimings"
   287  	cats := []string{"cat1", "cat2"}
   288  	catLabels := []string{"foo", "bar"}
   289  	timing := stats.NewMultiTimings(name, "help", cats)
   290  	timing.Add(catLabels, time.Duration(30*time.Millisecond))
   291  	timing.Add(catLabels, time.Duration(200*time.Millisecond))
   292  	timing.Add(catLabels, time.Duration(1*time.Second))
   293  
   294  	response := testMetricsHandler(t)
   295  	var s []string
   296  
   297  	s = append(s, fmt.Sprintf("%s_%s_bucket{%s=\"%s\",%s=\"%s\",le=\"0.0005\"} %d", namespace, name, cats[0], catLabels[0], cats[1], catLabels[1], 0))
   298  	s = append(s, fmt.Sprintf("%s_%s_bucket{%s=\"%s\",%s=\"%s\",le=\"0.001\"} %d", namespace, name, cats[0], catLabels[0], cats[1], catLabels[1], 0))
   299  	s = append(s, fmt.Sprintf("%s_%s_bucket{%s=\"%s\",%s=\"%s\",le=\"0.005\"} %d", namespace, name, cats[0], catLabels[0], cats[1], catLabels[1], 0))
   300  	s = append(s, fmt.Sprintf("%s_%s_bucket{%s=\"%s\",%s=\"%s\",le=\"0.01\"} %d", namespace, name, cats[0], catLabels[0], cats[1], catLabels[1], 0))
   301  	s = append(s, fmt.Sprintf("%s_%s_bucket{%s=\"%s\",%s=\"%s\",le=\"0.05\"} %d", namespace, name, cats[0], catLabels[0], cats[1], catLabels[1], 1))
   302  	s = append(s, fmt.Sprintf("%s_%s_bucket{%s=\"%s\",%s=\"%s\",le=\"0.1\"} %d", namespace, name, cats[0], catLabels[0], cats[1], catLabels[1], 1))
   303  	s = append(s, fmt.Sprintf("%s_%s_bucket{%s=\"%s\",%s=\"%s\",le=\"0.5\"} %d", namespace, name, cats[0], catLabels[0], cats[1], catLabels[1], 2))
   304  	s = append(s, fmt.Sprintf("%s_%s_bucket{%s=\"%s\",%s=\"%s\",le=\"1\"} %d", namespace, name, cats[0], catLabels[0], cats[1], catLabels[1], 3))
   305  	s = append(s, fmt.Sprintf("%s_%s_bucket{%s=\"%s\",%s=\"%s\",le=\"5\"} %d", namespace, name, cats[0], catLabels[0], cats[1], catLabels[1], 3))
   306  	s = append(s, fmt.Sprintf("%s_%s_bucket{%s=\"%s\",%s=\"%s\",le=\"10\"} %d", namespace, name, cats[0], catLabels[0], cats[1], catLabels[1], 3))
   307  	s = append(s, fmt.Sprintf("%s_%s_bucket{%s=\"%s\",%s=\"%s\",le=\"+Inf\"} %d", namespace, name, cats[0], catLabels[0], cats[1], catLabels[1], 3))
   308  	s = append(s, fmt.Sprintf("%s_%s_sum{%s=\"%s\",%s=\"%s\"} %s", namespace, name, cats[0], catLabels[0], cats[1], catLabels[1], "1.23"))
   309  	s = append(s, fmt.Sprintf("%s_%s_count{%s=\"%s\",%s=\"%s\"} %d", namespace, name, cats[0], catLabels[0], cats[1], catLabels[1], 3))
   310  
   311  	for _, line := range s {
   312  		if !strings.Contains(response.Body.String(), line) {
   313  			t.Fatalf("Expected result to contain %s, got %s", line, response.Body.String())
   314  		}
   315  	}
   316  }
   317  
   318  func TestPrometheusMultiTimings_PanicWrongLength(t *testing.T) {
   319  	defer func() {
   320  		if r := recover(); r == nil {
   321  			t.Errorf("The code did not panic when adding to inequal label lengths")
   322  		}
   323  	}()
   324  
   325  	c := stats.NewMultiTimings("name", "help", []string{"label1", "label2"})
   326  	c.Add([]string{"label1"}, time.Duration(100000000))
   327  }
   328  
   329  func TestPrometheusHistogram(t *testing.T) {
   330  	name := "blah_hist"
   331  	hist := stats.NewHistogram(name, "help", []int64{1, 5, 10})
   332  	hist.Add(2)
   333  	hist.Add(3)
   334  	hist.Add(6)
   335  
   336  	response := testMetricsHandler(t)
   337  	var s []string
   338  
   339  	s = append(s, fmt.Sprintf("%s_%s_bucket{le=\"1\"} %d", namespace, name, 0))
   340  	s = append(s, fmt.Sprintf("%s_%s_bucket{le=\"5\"} %d", namespace, name, 2))
   341  	s = append(s, fmt.Sprintf("%s_%s_bucket{le=\"10\"} %d", namespace, name, 3))
   342  	s = append(s, fmt.Sprintf("%s_%s_sum %d", namespace, name, 1))
   343  	s = append(s, fmt.Sprintf("%s_%s_count %d", namespace, name, 3))
   344  
   345  	for _, line := range s {
   346  		if !strings.Contains(response.Body.String(), line) {
   347  			t.Fatalf("Expected result to contain %s, got %s", line, response.Body.String())
   348  		}
   349  	}
   350  }
   351  
   352  func testMetricsHandler(t *testing.T) *httptest.ResponseRecorder {
   353  	req, _ := http.NewRequest("GET", "/metrics", nil)
   354  	response := httptest.NewRecorder()
   355  
   356  	promhttp.Handler().ServeHTTP(response, req)
   357  	return response
   358  }
   359  
   360  func TestPrometheusLabels(t *testing.T) {
   361  	m1 := stats.NewCountersWithSingleLabel("ThisIsMetric1", "helpstring1", "ThisIsALabel")
   362  	m1.Add("labelvalue1", 420)
   363  
   364  	m2 := stats.NewCountersWithMultiLabels("ThisIsMetric2", "helpstring2", []string{"ThisIsALabel"})
   365  	m2.Add([]string{"labelvalue2"}, 420)
   366  
   367  	response := testMetricsHandler(t)
   368  
   369  	expect := []string{
   370  		"namespace_this_is_metric1{this_is_a_label=\"labelvalue1\"} 420",
   371  		"namespace_this_is_metric2{this_is_a_label=\"labelvalue2\"} 420",
   372  	}
   373  	for _, line := range expect {
   374  		if !strings.Contains(response.Body.String(), line) {
   375  			t.Fatalf("Expected result to contain %s, got %s", line, response.Body.String())
   376  		}
   377  	}
   378  }
   379  
   380  func TestMain(m *testing.M) {
   381  	Init(namespace)
   382  	os.Exit(m.Run())
   383  }