vitess.io/vitess@v0.16.2/go/vt/servenv/exporter_test.go (about)

     1  /*
     2  Copyright 2020 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 servenv
    18  
    19  import (
    20  	"expvar"
    21  	"fmt"
    22  	"io"
    23  	"net"
    24  	"net/http"
    25  	"testing"
    26  	"time"
    27  
    28  	"github.com/stretchr/testify/assert"
    29  
    30  	"vitess.io/vitess/go/stats"
    31  )
    32  
    33  func TestURLPrefix(t *testing.T) {
    34  	assert.Equal(t, "", NewExporter("", "").URLPrefix())
    35  	assert.Equal(t, "/a", NewExporter("a", "").URLPrefix())
    36  }
    37  
    38  func TestHandleFunc(t *testing.T) {
    39  	// Listen on a random port
    40  	listener, err := net.Listen("tcp", "127.0.0.1:0")
    41  	if err != nil {
    42  		t.Fatalf("Cannot listen: %v", err)
    43  	}
    44  	defer listener.Close()
    45  	port := listener.Addr().(*net.TCPAddr).Port
    46  	go http.Serve(listener, nil)
    47  
    48  	ebd := NewExporter("", "")
    49  	ebd.HandleFunc("/path", func(w http.ResponseWriter, r *http.Request) {
    50  		w.Write([]byte("1"))
    51  	})
    52  	assert.Equal(t, "1", httpGet(t, fmt.Sprintf("http://localhost:%d/path", port)))
    53  	assert.Contains(t, httpGet(t, fmt.Sprintf("http://localhost:%d/debug/status", port)), "Status for")
    54  
    55  	ebd = NewExporter("a", "")
    56  	ebd.HandleFunc("/path", func(w http.ResponseWriter, r *http.Request) {
    57  		w.Write([]byte("2"))
    58  	})
    59  	assert.Equal(t, "2", httpGet(t, fmt.Sprintf("http://localhost:%d/a/path", port)))
    60  	assert.Contains(t, httpGet(t, fmt.Sprintf("http://localhost:%d/debug/status", port)), "Status for")
    61  
    62  	ebd.HandleFunc("/path", func(w http.ResponseWriter, r *http.Request) {
    63  		w.Write([]byte("3"))
    64  	})
    65  	assert.Equal(t, "3", httpGet(t, fmt.Sprintf("http://localhost:%d/a/path", port)))
    66  	assert.Contains(t, httpGet(t, fmt.Sprintf("http://localhost:%d/a/debug/status", port)), "Status for")
    67  
    68  	ebd = NewExporter("a", "")
    69  	ebd.HandleFunc("/path", func(w http.ResponseWriter, r *http.Request) {
    70  		w.Write([]byte("4"))
    71  	})
    72  	assert.Equal(t, "4", httpGet(t, fmt.Sprintf("http://localhost:%d/a/path", port)))
    73  
    74  	ebd = NewExporter("b", "")
    75  	ebd.HandleFunc("/path", func(w http.ResponseWriter, r *http.Request) {
    76  		w.Write([]byte("5"))
    77  	})
    78  	assert.Equal(t, "5", httpGet(t, fmt.Sprintf("http://localhost:%d/b/path", port)))
    79  	assert.Contains(t, httpGet(t, fmt.Sprintf("http://localhost:%d/b/debug/status", port)), "Status for")
    80  	// Ensure "a" is still the same.
    81  	assert.Equal(t, "4", httpGet(t, fmt.Sprintf("http://localhost:%d/a/path", port)))
    82  	assert.Contains(t, httpGet(t, fmt.Sprintf("http://localhost:%d/a/debug/status", port)), "Status for")
    83  }
    84  
    85  func httpGet(t *testing.T, url string) string {
    86  	t.Helper()
    87  
    88  	resp, err := http.Get(url)
    89  	if err != nil {
    90  		t.Fatal(err)
    91  	}
    92  	defer resp.Body.Close()
    93  
    94  	body, err := io.ReadAll(resp.Body)
    95  	if err != nil {
    96  		t.Fatal(err)
    97  	}
    98  	return string(body)
    99  }
   100  
   101  func TestCountersFuncWithMultiLabels(t *testing.T) {
   102  	ebd := NewExporter("", "")
   103  	ebd.NewCountersFuncWithMultiLabels("gcfwml", "", []string{"l"}, func() map[string]int64 { return map[string]int64{"a": 1} })
   104  	assert.Equal(t, `{"a": 1}`, expvar.Get("gcfwml").String())
   105  
   106  	ebd = NewExporter("i1", "label")
   107  
   108  	// Ensure anonymous vars don't cause panics.
   109  	ebd.NewCountersFuncWithMultiLabels("", "", []string{"l"}, func() map[string]int64 { return map[string]int64{"a": 2} })
   110  	ebd.NewCountersFuncWithMultiLabels("", "", []string{"l"}, func() map[string]int64 { return map[string]int64{"a": 3} })
   111  
   112  	// Ensure reuse of global var is ignored.
   113  	ebd.NewCountersFuncWithMultiLabels("gcfwml", "", []string{"l"}, func() map[string]int64 { return map[string]int64{"a": 2} })
   114  	assert.Equal(t, `{"a": 1}`, expvar.Get("gcfwml").String())
   115  
   116  	ebd.NewCountersFuncWithMultiLabels("lcfwml", "", []string{"l"}, func() map[string]int64 { return map[string]int64{"a": 4} })
   117  	assert.Equal(t, `{"i1.a": 4}`, expvar.Get("lcfwml").String())
   118  
   119  	// Ensure var gets replaced.
   120  	ebd.NewCountersFuncWithMultiLabels("lcfwml", "", []string{"l"}, func() map[string]int64 { return map[string]int64{"a": 5} })
   121  	assert.Equal(t, `{"i1.a": 5}`, expvar.Get("lcfwml").String())
   122  
   123  	ebd = NewExporter("i2", "label")
   124  	ebd.NewCountersFuncWithMultiLabels("lcfwml", "", []string{"l"}, func() map[string]int64 { return map[string]int64{"a": 6} })
   125  	assert.Contains(t, expvar.Get("lcfwml").String(), `"i1.a": 5`)
   126  	assert.Contains(t, expvar.Get("lcfwml").String(), `"i2.a": 6`)
   127  }
   128  
   129  func TestGaugesFuncWithMultiLabels(t *testing.T) {
   130  	ebd := NewExporter("", "")
   131  	ebd.NewGaugesFuncWithMultiLabels("ggfwml", "", []string{"l"}, func() map[string]int64 { return map[string]int64{"a": 1} })
   132  	assert.Equal(t, `{"a": 1}`, expvar.Get("ggfwml").String())
   133  
   134  	ebd = NewExporter("i1", "label")
   135  
   136  	// Ensure anonymous vars don't cause panics.
   137  	ebd.NewGaugesFuncWithMultiLabels("", "", []string{"l"}, func() map[string]int64 { return map[string]int64{"a": 2} })
   138  	ebd.NewGaugesFuncWithMultiLabels("", "", []string{"l"}, func() map[string]int64 { return map[string]int64{"a": 3} })
   139  
   140  	// Ensure reuse of global var is ignored.
   141  	ebd.NewGaugesFuncWithMultiLabels("ggfwml", "", []string{"l"}, func() map[string]int64 { return map[string]int64{"a": 1} })
   142  	assert.Equal(t, `{"a": 1}`, expvar.Get("ggfwml").String())
   143  
   144  	ebd.NewGaugesFuncWithMultiLabels("lgfwml", "", []string{"l"}, func() map[string]int64 { return map[string]int64{"a": 4} })
   145  	assert.Equal(t, `{"i1.a": 4}`, expvar.Get("lgfwml").String())
   146  
   147  	// Ensure var gets replaced.
   148  	ebd.NewGaugesFuncWithMultiLabels("lgfwml", "", []string{"l"}, func() map[string]int64 { return map[string]int64{"a": 5} })
   149  	assert.Equal(t, `{"i1.a": 5}`, expvar.Get("lgfwml").String())
   150  
   151  	ebd = NewExporter("i2", "label")
   152  	ebd.NewGaugesFuncWithMultiLabels("lgfwml", "", []string{"l"}, func() map[string]int64 { return map[string]int64{"a": 6} })
   153  	assert.Contains(t, expvar.Get("lgfwml").String(), `"i1.a": 5`)
   154  	assert.Contains(t, expvar.Get("lgfwml").String(), `"i2.a": 6`)
   155  }
   156  
   157  func TestCounter(t *testing.T) {
   158  	ebd := NewExporter("", "")
   159  	c := ebd.NewCounter("gcounter", "")
   160  	c.Add(1)
   161  	assert.Equal(t, "1", expvar.Get("gcounter").String())
   162  
   163  	ebd = NewExporter("i1", "label")
   164  
   165  	// Ensure anonymous vars don't cause panics.
   166  	ebd.NewCounter("", "")
   167  	ebd.NewCounter("", "")
   168  
   169  	// Ensure global var gets reused.
   170  	c = ebd.NewCounter("gcounter", "")
   171  	c.Add(1)
   172  	assert.Equal(t, "2", expvar.Get("gcounter").String())
   173  
   174  	c = ebd.NewCounter("lcounter", "")
   175  	c.Add(4)
   176  	assert.Equal(t, `{"i1": 4}`, expvar.Get("lcounter").String())
   177  
   178  	// Ensure var gets reused.
   179  	c = ebd.NewCounter("lcounter", "")
   180  	c.Add(5)
   181  	assert.Equal(t, `{"i1": 9}`, expvar.Get("lcounter").String())
   182  
   183  	ebd = NewExporter("i2", "label")
   184  	c = ebd.NewCounter("lcounter", "")
   185  	c.Add(6)
   186  	assert.Contains(t, expvar.Get("lcounter").String(), `"i1": 9`)
   187  	assert.Contains(t, expvar.Get("lcounter").String(), `"i2": 6`)
   188  }
   189  
   190  func TestGauge(t *testing.T) {
   191  	ebd := NewExporter("", "")
   192  	c := ebd.NewGauge("ggauge", "")
   193  	c.Set(1)
   194  	assert.Equal(t, "1", expvar.Get("ggauge").String())
   195  
   196  	ebd = NewExporter("i1", "label")
   197  
   198  	// Ensure anonymous vars don't cause panics.
   199  	ebd.NewGauge("", "")
   200  	ebd.NewGauge("", "")
   201  
   202  	// Ensure global var gets reused.
   203  	c = ebd.NewGauge("ggauge", "")
   204  	c.Set(2)
   205  	assert.Equal(t, "2", expvar.Get("ggauge").String())
   206  
   207  	c = ebd.NewGauge("lgauge", "")
   208  	c.Set(4)
   209  	assert.Equal(t, `{"i1": 4}`, expvar.Get("lgauge").String())
   210  
   211  	// Ensure var gets reused.
   212  	c = ebd.NewGauge("lgauge", "")
   213  	assert.Equal(t, `{"i1": 4}`, expvar.Get("lgauge").String())
   214  	c.Set(5)
   215  	assert.Equal(t, `{"i1": 5}`, expvar.Get("lgauge").String())
   216  
   217  	ebd = NewExporter("i2", "label")
   218  	c = ebd.NewGauge("lgauge", "")
   219  	c.Set(6)
   220  	assert.Contains(t, expvar.Get("lgauge").String(), `"i1": 5`)
   221  	assert.Contains(t, expvar.Get("lgauge").String(), `"i2": 6`)
   222  }
   223  
   224  func TestCounterFunc(t *testing.T) {
   225  	ebd := NewExporter("", "")
   226  	ebd.NewCounterFunc("gcf", "", func() int64 { return 1 })
   227  	assert.Equal(t, "1", expvar.Get("gcf").String())
   228  
   229  	ebd = NewExporter("i1", "label")
   230  
   231  	// Ensure anonymous vars don't cause panics.
   232  	ebd.NewCounterFunc("", "", func() int64 { return 2 })
   233  	ebd.NewCounterFunc("", "", func() int64 { return 3 })
   234  
   235  	// Ensure reuse of global var is ignored.
   236  	ebd.NewCounterFunc("gcf", "", func() int64 { return 2 })
   237  	assert.Equal(t, "1", expvar.Get("gcf").String())
   238  
   239  	ebd.NewCounterFunc("lcf", "", func() int64 { return 4 })
   240  	assert.Equal(t, `{"i1": 4}`, expvar.Get("lcf").String())
   241  
   242  	// Ensure var gets replaced.
   243  	ebd.NewCounterFunc("lcf", "", func() int64 { return 5 })
   244  	assert.Equal(t, `{"i1": 5}`, expvar.Get("lcf").String())
   245  
   246  	ebd = NewExporter("i2", "label")
   247  	ebd.NewCounterFunc("lcf", "", func() int64 { return 6 })
   248  	assert.Contains(t, expvar.Get("lcf").String(), `"i1": 5`)
   249  	assert.Contains(t, expvar.Get("lcf").String(), `"i2": 6`)
   250  }
   251  
   252  func TestGaugeFunc(t *testing.T) {
   253  	ebd := NewExporter("", "")
   254  	ebd.NewGaugeFunc("ggf", "", func() int64 { return 1 })
   255  	assert.Equal(t, "1", expvar.Get("ggf").String())
   256  
   257  	ebd = NewExporter("i1", "label")
   258  
   259  	// Ensure anonymous vars don't cause panics.
   260  	ebd.NewGaugeFunc("", "", func() int64 { return 2 })
   261  	ebd.NewGaugeFunc("", "", func() int64 { return 3 })
   262  
   263  	// Ensure reuse of global var is ignored.
   264  	ebd.NewGaugeFunc("ggf", "", func() int64 { return 2 })
   265  	assert.Equal(t, "1", expvar.Get("ggf").String())
   266  
   267  	ebd.NewGaugeFunc("lgf", "", func() int64 { return 4 })
   268  	assert.Equal(t, `{"i1": 4}`, expvar.Get("lgf").String())
   269  
   270  	// Ensure var gets replaced.
   271  	ebd.NewGaugeFunc("lgf", "", func() int64 { return 5 })
   272  	assert.Equal(t, `{"i1": 5}`, expvar.Get("lgf").String())
   273  
   274  	ebd = NewExporter("i2", "label")
   275  	ebd.NewGaugeFunc("lgf", "", func() int64 { return 6 })
   276  	assert.Contains(t, expvar.Get("lgf").String(), `"i1": 5`)
   277  	assert.Contains(t, expvar.Get("lgf").String(), `"i2": 6`)
   278  }
   279  
   280  func TestCounterDurationFunc(t *testing.T) {
   281  	ebd := NewExporter("", "")
   282  	ebd.NewCounterDurationFunc("gcduration", "", func() time.Duration { return 1 })
   283  	assert.Equal(t, "1", expvar.Get("gcduration").String())
   284  
   285  	ebd = NewExporter("i1", "label")
   286  
   287  	// Ensure anonymous vars don't cause panics.
   288  	ebd.NewCounterDurationFunc("", "", func() time.Duration { return 2 })
   289  	ebd.NewCounterDurationFunc("", "", func() time.Duration { return 3 })
   290  
   291  	// Ensure reuse of global var is ignored.
   292  	ebd.NewCounterDurationFunc("gcduration", "", func() time.Duration { return 2 })
   293  	assert.Equal(t, "1", expvar.Get("gcduration").String())
   294  
   295  	ebd.NewCounterDurationFunc("lcduration", "", func() time.Duration { return 4 })
   296  	assert.Equal(t, `{"i1": 4}`, expvar.Get("lcduration").String())
   297  
   298  	// Ensure var gets replaced.
   299  	ebd.NewCounterDurationFunc("lcduration", "", func() time.Duration { return 5 })
   300  	assert.Equal(t, `{"i1": 5}`, expvar.Get("lcduration").String())
   301  
   302  	ebd = NewExporter("i2", "label")
   303  	ebd.NewCounterDurationFunc("lcduration", "", func() time.Duration { return 6 })
   304  	assert.Contains(t, expvar.Get("lcduration").String(), `"i1": 5`)
   305  	assert.Contains(t, expvar.Get("lcduration").String(), `"i2": 6`)
   306  }
   307  
   308  func TestGaugeDurationFunc(t *testing.T) {
   309  	ebd := NewExporter("", "")
   310  	ebd.NewGaugeDurationFunc("ggduration", "", func() time.Duration { return 1 })
   311  	assert.Equal(t, "1", expvar.Get("ggduration").String())
   312  
   313  	ebd = NewExporter("i1", "label")
   314  
   315  	// Ensure anonymous vars don't cause panics.
   316  	ebd.NewGaugeDurationFunc("", "", func() time.Duration { return 2 })
   317  	ebd.NewGaugeDurationFunc("", "", func() time.Duration { return 3 })
   318  
   319  	// Ensure reuse of global var is ignored.
   320  	ebd.NewGaugeDurationFunc("ggduration", "", func() time.Duration { return 2 })
   321  	assert.Equal(t, "1", expvar.Get("ggduration").String())
   322  
   323  	ebd.NewGaugeDurationFunc("lgduration", "", func() time.Duration { return 4 })
   324  	assert.Equal(t, `{"i1": 4}`, expvar.Get("lgduration").String())
   325  
   326  	// Ensure var gets replaced.
   327  	ebd.NewGaugeDurationFunc("lgduration", "", func() time.Duration { return 5 })
   328  	assert.Equal(t, `{"i1": 5}`, expvar.Get("lgduration").String())
   329  
   330  	ebd = NewExporter("i2", "label")
   331  	ebd.NewGaugeDurationFunc("lgduration", "", func() time.Duration { return 6 })
   332  	assert.Contains(t, expvar.Get("lgduration").String(), `"i1": 5`)
   333  	assert.Contains(t, expvar.Get("lgduration").String(), `"i2": 6`)
   334  }
   335  
   336  func TestCountersWithSingleLabel(t *testing.T) {
   337  	ebd := NewExporter("", "")
   338  	g := ebd.NewCountersWithSingleLabel("gcwsl", "", "l")
   339  	g.Add("a", 1)
   340  	assert.Equal(t, `{"a": 1}`, expvar.Get("gcwsl").String())
   341  
   342  	ebd = NewExporter("i1", "label")
   343  
   344  	// Ensure anonymous vars don't cause panics.
   345  	ebd.NewCountersWithSingleLabel("", "", "l")
   346  	ebd.NewCountersWithSingleLabel("", "", "l")
   347  
   348  	// Ensure global var gets reused.
   349  	g = ebd.NewCountersWithSingleLabel("gcwsl", "", "l")
   350  	g.Add("a", 1)
   351  	assert.Equal(t, `{"a": 2}`, expvar.Get("gcwsl").String())
   352  
   353  	g = ebd.NewCountersWithSingleLabel("lcwsl", "", "l")
   354  	g.Add("a", 4)
   355  	assert.Equal(t, `{"i1.a": 4}`, expvar.Get("lcwsl").String())
   356  
   357  	// Ensure var gets reused.
   358  	g = ebd.NewCountersWithSingleLabel("lcwsl", "", "l")
   359  	g.Add("a", 5)
   360  	assert.Equal(t, `{"i1.a": 9}`, expvar.Get("lcwsl").String())
   361  
   362  	ebd = NewExporter("i2", "label")
   363  	g = ebd.NewCountersWithSingleLabel("lcwsl", "", "l")
   364  	g.Add("a", 6)
   365  	assert.Contains(t, expvar.Get("lcwsl").String(), `"i1.a": 9`)
   366  	assert.Contains(t, expvar.Get("lcwsl").String(), `"i2.a": 6`)
   367  }
   368  
   369  func TestGaugesWithSingleLabel(t *testing.T) {
   370  	ebd := NewExporter("", "")
   371  	g := ebd.NewGaugesWithSingleLabel("ggwsl", "", "l")
   372  	g.Set("a", 1)
   373  	assert.Equal(t, `{"a": 1}`, expvar.Get("ggwsl").String())
   374  
   375  	ebd = NewExporter("i1", "label")
   376  
   377  	// Ensure anonymous vars don't cause panics.
   378  	ebd.NewGaugesWithSingleLabel("", "", "l")
   379  	ebd.NewGaugesWithSingleLabel("", "", "l")
   380  
   381  	// Ensure reuse of global var is ignored.
   382  	g = ebd.NewGaugesWithSingleLabel("ggwsl", "", "l")
   383  	g.Set("a", 2)
   384  	assert.Equal(t, `{"a": 1}`, expvar.Get("ggwsl").String())
   385  
   386  	g = ebd.NewGaugesWithSingleLabel("lgwsl", "", "l")
   387  	g.Set("a", 4)
   388  	assert.Equal(t, `{"i1.a": 4}`, expvar.Get("lgwsl").String())
   389  
   390  	// Ensure var gets reused.
   391  	g = ebd.NewGaugesWithSingleLabel("lgwsl", "", "l")
   392  	assert.Equal(t, `{"i1.a": 4}`, expvar.Get("lgwsl").String())
   393  	g.Set("a", 5)
   394  	assert.Equal(t, `{"i1.a": 5}`, expvar.Get("lgwsl").String())
   395  
   396  	ebd = NewExporter("i2", "label")
   397  	g = ebd.NewGaugesWithSingleLabel("lgwsl", "", "l")
   398  	g.Set("a", 6)
   399  	assert.Contains(t, expvar.Get("lgwsl").String(), `"i1.a": 5`)
   400  	assert.Contains(t, expvar.Get("lgwsl").String(), `"i2.a": 6`)
   401  }
   402  
   403  func TestCountersWithMultiLabels(t *testing.T) {
   404  	ebd := NewExporter("", "")
   405  	g := ebd.NewCountersWithMultiLabels("gcwml", "", []string{"l"})
   406  	g.Add([]string{"a"}, 1)
   407  	assert.Equal(t, `{"a": 1}`, expvar.Get("gcwml").String())
   408  
   409  	ebd = NewExporter("i1", "label")
   410  
   411  	// Ensure anonymous vars don't cause panics.
   412  	ebd.NewCountersWithMultiLabels("", "", []string{"l"})
   413  	ebd.NewCountersWithMultiLabels("", "", []string{"l"})
   414  
   415  	// Ensure global var gets reused.
   416  	g = ebd.NewCountersWithMultiLabels("gcwml", "", []string{"l"})
   417  	g.Add([]string{"a"}, 1)
   418  	assert.Equal(t, `{"a": 2}`, expvar.Get("gcwml").String())
   419  
   420  	g = ebd.NewCountersWithMultiLabels("lcwml", "", []string{"l"})
   421  	g.Add([]string{"a"}, 4)
   422  	assert.Equal(t, `{"i1.a": 4}`, expvar.Get("lcwml").String())
   423  
   424  	// Ensure var gets reused.
   425  	g = ebd.NewCountersWithMultiLabels("lcwml", "", []string{"l"})
   426  	g.Add([]string{"a"}, 5)
   427  	assert.Equal(t, `{"i1.a": 9}`, expvar.Get("lcwml").String())
   428  
   429  	ebd = NewExporter("i2", "label")
   430  	g = ebd.NewCountersWithMultiLabels("lcwml", "", []string{"l"})
   431  	g.Add([]string{"a"}, 6)
   432  	assert.Contains(t, expvar.Get("lcwml").String(), `"i1.a": 9`)
   433  	assert.Contains(t, expvar.Get("lcwml").String(), `"i2.a": 6`)
   434  }
   435  
   436  func TestGaugesWithMultiLabels(t *testing.T) {
   437  	ebd := NewExporter("", "")
   438  	g := ebd.NewGaugesWithMultiLabels("ggwml", "", []string{"l"})
   439  	g.Set([]string{"a"}, 1)
   440  	assert.Equal(t, `{"a": 1}`, expvar.Get("ggwml").String())
   441  
   442  	ebd = NewExporter("i1", "label")
   443  
   444  	// Ensure anonymous vars don't cause panics.
   445  	ebd.NewGaugesWithMultiLabels("", "", []string{"l"})
   446  	ebd.NewGaugesWithMultiLabels("", "", []string{"l"})
   447  
   448  	// Ensure reuse of global var is ignored.
   449  	g = ebd.NewGaugesWithMultiLabels("ggwml", "", []string{"l"})
   450  	g.Set([]string{"a"}, 2)
   451  	assert.Equal(t, `{"a": 1}`, expvar.Get("ggwml").String())
   452  
   453  	g = ebd.NewGaugesWithMultiLabels("lgwml", "", []string{"l"})
   454  	g.Set([]string{"a"}, 4)
   455  	assert.Equal(t, `{"i1.a": 4}`, expvar.Get("lgwml").String())
   456  
   457  	// Ensure var gets reused.
   458  	g = ebd.NewGaugesWithMultiLabels("lgwml", "", []string{"l"})
   459  	assert.Equal(t, `{"i1.a": 4}`, expvar.Get("lgwml").String())
   460  	g.Set([]string{"a"}, 5)
   461  	assert.Equal(t, `{"i1.a": 5}`, expvar.Get("lgwml").String())
   462  
   463  	ebd = NewExporter("i2", "label")
   464  	g = ebd.NewGaugesWithMultiLabels("lgwml", "", []string{"l"})
   465  	g.Set([]string{"a"}, 6)
   466  	assert.Contains(t, expvar.Get("lgwml").String(), `"i1.a": 5`)
   467  	assert.Contains(t, expvar.Get("lgwml").String(), `"i2.a": 6`)
   468  }
   469  
   470  func TestTimings(t *testing.T) {
   471  	ebd := NewExporter("", "")
   472  	g := ebd.NewTimings("gtimings", "", "l")
   473  	g.Add("a", 1)
   474  	assert.Contains(t, expvar.Get("gtimings").String(), `"TotalCount":1`)
   475  	g.Record("a", time.Now())
   476  	assert.Contains(t, expvar.Get("gtimings").String(), `"TotalCount":2`)
   477  
   478  	ebd = NewExporter("i1", "label")
   479  
   480  	// Ensure anonymous vars don't cause panics.
   481  	ebd.NewTimings("", "", "l")
   482  	ebd.NewTimings("", "", "l")
   483  
   484  	// Ensure global var gets reused.
   485  	g = ebd.NewTimings("gtimings", "", "l")
   486  	g.Add("a", 1)
   487  	assert.Contains(t, expvar.Get("gtimings").String(), `"TotalCount":3`)
   488  
   489  	g = ebd.NewTimings("ltimings", "", "l")
   490  	g.Add("a", 1)
   491  	g.Add("a", 1)
   492  	assert.Contains(t, expvar.Get("ltimings").String(), `i1.a`)
   493  	assert.Contains(t, expvar.Get("ltimings").String(), `"TotalCount":2`)
   494  
   495  	// Ensure var gets reused.
   496  	g = ebd.NewTimings("ltimings", "", "l")
   497  	g.Add("a", 1)
   498  	assert.Contains(t, expvar.Get("ltimings").String(), `i1.a`)
   499  	assert.Contains(t, expvar.Get("ltimings").String(), `"TotalCount":3`)
   500  	g.Record("a", time.Now())
   501  	assert.Contains(t, expvar.Get("ltimings").String(), `"TotalCount":4`)
   502  
   503  	ebd = NewExporter("i2", "label")
   504  	g = ebd.NewTimings("ltimings", "", "l")
   505  	g.Add("a", 1)
   506  	assert.Contains(t, expvar.Get("ltimings").String(), `i1.a`)
   507  	assert.Contains(t, expvar.Get("ltimings").String(), `i2.a`)
   508  	assert.Contains(t, expvar.Get("ltimings").String(), `"TotalCount":5`)
   509  
   510  	want := map[string]int64{
   511  		"All":  5,
   512  		"i1.a": 4,
   513  		"i2.a": 1,
   514  	}
   515  	assert.Equal(t, want, g.Counts())
   516  }
   517  
   518  func TestMultiTimings(t *testing.T) {
   519  	ebd := NewExporter("", "")
   520  	g := ebd.NewMultiTimings("gmtimings", "", []string{"l"})
   521  	g.Add([]string{"a"}, 1)
   522  	assert.Contains(t, expvar.Get("gmtimings").String(), `"TotalCount":1`)
   523  	g.Record([]string{"a"}, time.Now())
   524  	assert.Contains(t, expvar.Get("gmtimings").String(), `"TotalCount":2`)
   525  
   526  	ebd = NewExporter("i1", "label")
   527  
   528  	// Ensure anonymous vars don't cause panics.
   529  	ebd.NewMultiTimings("", "", []string{"l"})
   530  	ebd.NewMultiTimings("", "", []string{"l"})
   531  
   532  	// Ensure global var gets reused.
   533  	g = ebd.NewMultiTimings("gmtimings", "", []string{"l"})
   534  	g.Add([]string{"a"}, 1)
   535  	assert.Contains(t, expvar.Get("gmtimings").String(), `"TotalCount":3`)
   536  
   537  	g = ebd.NewMultiTimings("lmtimings", "", []string{"l"})
   538  	g.Add([]string{"a"}, 1)
   539  	g.Add([]string{"a"}, 1)
   540  	assert.Contains(t, expvar.Get("lmtimings").String(), `i1.a`)
   541  	assert.Contains(t, expvar.Get("lmtimings").String(), `"TotalCount":2`)
   542  	g.Record([]string{"a"}, time.Now())
   543  	assert.Contains(t, expvar.Get("lmtimings").String(), `"TotalCount":3`)
   544  
   545  	// Ensure var gets reused.
   546  	g = ebd.NewMultiTimings("lmtimings", "", []string{"l"})
   547  	g.Add([]string{"a"}, 1)
   548  	assert.Contains(t, expvar.Get("lmtimings").String(), `i1.a`)
   549  	assert.Contains(t, expvar.Get("lmtimings").String(), `"TotalCount":4`)
   550  
   551  	ebd = NewExporter("i2", "label")
   552  	g = ebd.NewMultiTimings("lmtimings", "", []string{"l"})
   553  	g.Add([]string{"a"}, 1)
   554  	assert.Contains(t, expvar.Get("lmtimings").String(), `i1.a`)
   555  	assert.Contains(t, expvar.Get("lmtimings").String(), `i2.a`)
   556  	assert.Contains(t, expvar.Get("lmtimings").String(), `"TotalCount":5`)
   557  
   558  	want := map[string]int64{
   559  		"All":  5,
   560  		"i1.a": 4,
   561  		"i2.a": 1,
   562  	}
   563  	assert.Equal(t, want, g.Counts())
   564  }
   565  
   566  func TestRates(t *testing.T) {
   567  	ebd := NewExporter("", "")
   568  	tm := ebd.NewMultiTimings("gratetimings", "", []string{"l"})
   569  	ebd.NewRates("grates", tm, 15*60/5, 5*time.Second)
   570  	assert.Equal(t, "{}", expvar.Get("grates").String())
   571  
   572  	ebd = NewExporter("i1", "label")
   573  
   574  	// Ensure anonymous vars don't cause panics.
   575  	ebd.NewRates("", tm, 15*60/5, 5*time.Second)
   576  	ebd.NewRates("", tm, 15*60/5, 5*time.Second)
   577  
   578  	// Ensure global var gets reused.
   579  	ebd.NewRates("grates", tm, 15*60/5, 5*time.Second)
   580  	assert.Equal(t, "{}", expvar.Get("grates").String())
   581  
   582  	// Ensure var gets reused.
   583  	rates1 := ebd.NewRates("lrates", tm, 15*60/5, 5*time.Second)
   584  	rates2 := ebd.NewRates("lrates", tm, 15*60/5, 5*time.Second)
   585  	assert.True(t, rates2 == rates1)
   586  
   587  	ebd = NewExporter("i2", "label")
   588  	rates3 := ebd.NewRates("lrates", tm, 15*60/5, 5*time.Second)
   589  	assert.True(t, rates3 != rates1)
   590  }
   591  
   592  func TestHistogram(t *testing.T) {
   593  	ebd := NewExporter("", "")
   594  	g := ebd.NewHistogram("ghistogram", "", []int64{10})
   595  	g.Add(1)
   596  	assert.Contains(t, expvar.Get("ghistogram").String(), `{"10": 1, "inf": 0, "Count": 1, "Total": 1}`)
   597  
   598  	ebd = NewExporter("i1", "label")
   599  
   600  	// Ensure anonymous vars don't cause panics.
   601  	ebd.NewHistogram("", "", []int64{10})
   602  	ebd.NewHistogram("", "", []int64{10})
   603  
   604  	// Ensure reuse of global var doesn't panic.
   605  	_ = ebd.NewHistogram("ghistogram", "", []int64{10})
   606  
   607  	g = ebd.NewHistogram("lhistogram", "", []int64{10})
   608  	g.Add(1)
   609  	g.Add(1)
   610  	assert.Contains(t, expvar.Get("lmtimings").String(), `i1`)
   611  	assert.Contains(t, expvar.Get("lhistogram").String(), `{"10": 2, "inf": 0, "Count": 2, "Total": 2}`)
   612  
   613  	// Ensure var gets replaced.
   614  	g = ebd.NewHistogram("lhistogram", "", []int64{10})
   615  	g.Add(1)
   616  	assert.Contains(t, expvar.Get("lmtimings").String(), `i1`)
   617  	assert.Contains(t, expvar.Get("lhistogram").String(), `{"10": 1, "inf": 0, "Count": 1, "Total": 1}`)
   618  }
   619  
   620  func TestPublish(t *testing.T) {
   621  	ebd := NewExporter("", "")
   622  	s := stats.NewString("")
   623  	ebd.Publish("gpub", s)
   624  	s.Set("1")
   625  	assert.Equal(t, `"1"`, expvar.Get("gpub").String())
   626  
   627  	ebd = NewExporter("i1", "label")
   628  	ebd.Publish("lpub", s)
   629  	assert.Equal(t, `{"i1": "1"}`, expvar.Get("lpub").String())
   630  
   631  	// Ensure reuse of global var doesn't panic.
   632  	ebd.Publish("gpub", s)
   633  
   634  	ebd = NewExporter("i2", "label")
   635  	ebd.Publish("lpub", s)
   636  	assert.Contains(t, expvar.Get("lpub").String(), `"i1": "1"`)
   637  	assert.Contains(t, expvar.Get("lpub").String(), `"i2": "1"`)
   638  }