vitess.io/vitess@v0.16.2/go/stats/counters.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 stats
    18  
    19  import (
    20  	"bytes"
    21  	"fmt"
    22  	"strings"
    23  	"sync"
    24  )
    25  
    26  // counters is similar to expvar.Map, except that it doesn't allow floats.
    27  // It is used to build CountersWithSingleLabel and GaugesWithSingleLabel.
    28  type counters struct {
    29  	mu     sync.Mutex
    30  	counts map[string]int64
    31  
    32  	help string
    33  }
    34  
    35  func (c *counters) String() string {
    36  	c.mu.Lock()
    37  	defer c.mu.Unlock()
    38  
    39  	b := &strings.Builder{}
    40  	fmt.Fprintf(b, "{")
    41  	prefix := ""
    42  	for k, v := range c.counts {
    43  		fmt.Fprintf(b, "%s%q: %v", prefix, k, v)
    44  		prefix = ", "
    45  	}
    46  	fmt.Fprintf(b, "}")
    47  	return b.String()
    48  }
    49  
    50  func (c *counters) add(name string, value int64) {
    51  	c.mu.Lock()
    52  	defer c.mu.Unlock()
    53  	c.counts[name] = c.counts[name] + value
    54  }
    55  
    56  func (c *counters) set(name string, value int64) {
    57  	c.mu.Lock()
    58  	defer c.mu.Unlock()
    59  	c.counts[name] = value
    60  }
    61  
    62  func (c *counters) reset() {
    63  	c.mu.Lock()
    64  	defer c.mu.Unlock()
    65  	c.counts = make(map[string]int64)
    66  }
    67  
    68  // ZeroAll zeroes out all values
    69  func (c *counters) ZeroAll() {
    70  	c.mu.Lock()
    71  	defer c.mu.Unlock()
    72  
    73  	for k := range c.counts {
    74  		c.counts[k] = 0
    75  	}
    76  }
    77  
    78  // Counts returns a copy of the Counters' map.
    79  func (c *counters) Counts() map[string]int64 {
    80  	c.mu.Lock()
    81  	defer c.mu.Unlock()
    82  
    83  	counts := make(map[string]int64, len(c.counts))
    84  	for k, v := range c.counts {
    85  		counts[k] = v
    86  	}
    87  	return counts
    88  }
    89  
    90  // Help returns the help string.
    91  func (c *counters) Help() string {
    92  	return c.help
    93  }
    94  
    95  // CountersWithSingleLabel tracks multiple counter values for a single
    96  // dimension ("label").
    97  // It provides a Counts method which can be used for tracking rates.
    98  type CountersWithSingleLabel struct {
    99  	counters
   100  	label         string
   101  	labelCombined bool
   102  }
   103  
   104  // NewCountersWithSingleLabel create a new Counters instance.
   105  // If name is set, the variable gets published.
   106  // The function also accepts an optional list of tags that pre-creates them
   107  // initialized to 0.
   108  // label is a category name used to organize the tags. It is currently only
   109  // used by Prometheus, but not by the expvar package.
   110  func NewCountersWithSingleLabel(name, help, label string, tags ...string) *CountersWithSingleLabel {
   111  	c := &CountersWithSingleLabel{
   112  		counters: counters{
   113  			counts: make(map[string]int64),
   114  			help:   help,
   115  		},
   116  		label:         label,
   117  		labelCombined: IsDimensionCombined(label),
   118  	}
   119  
   120  	if c.labelCombined {
   121  		c.counts[StatsAllStr] = 0
   122  	} else {
   123  		for _, tag := range tags {
   124  			c.counts[tag] = 0
   125  		}
   126  	}
   127  	if name != "" {
   128  		publish(name, c)
   129  	}
   130  	return c
   131  }
   132  
   133  // Label returns the label name.
   134  func (c *CountersWithSingleLabel) Label() string {
   135  	return c.label
   136  }
   137  
   138  // Add adds a value to a named counter.
   139  func (c *CountersWithSingleLabel) Add(name string, value int64) {
   140  	if c.labelCombined {
   141  		name = StatsAllStr
   142  	}
   143  	c.counters.add(name, value)
   144  }
   145  
   146  // Reset resets the value for the name.
   147  func (c *CountersWithSingleLabel) Reset(name string) {
   148  	if c.labelCombined {
   149  		name = StatsAllStr
   150  	}
   151  	c.counters.set(name, 0)
   152  }
   153  
   154  // ResetAll clears the counters
   155  func (c *CountersWithSingleLabel) ResetAll() {
   156  	c.counters.reset()
   157  }
   158  
   159  // CountersWithMultiLabels is a multidimensional counters implementation.
   160  // Internally, each tuple of dimensions ("labels") is stored as a single
   161  // label value where all label values are joined with ".".
   162  type CountersWithMultiLabels struct {
   163  	counters
   164  	labels         []string
   165  	combinedLabels []bool
   166  }
   167  
   168  // NewCountersWithMultiLabels creates a new CountersWithMultiLabels
   169  // instance, and publishes it if name is set.
   170  func NewCountersWithMultiLabels(name, help string, labels []string) *CountersWithMultiLabels {
   171  	t := &CountersWithMultiLabels{
   172  		counters: counters{
   173  			counts: make(map[string]int64),
   174  			help:   help},
   175  		labels:         labels,
   176  		combinedLabels: make([]bool, len(labels)),
   177  	}
   178  	for i, label := range labels {
   179  		t.combinedLabels[i] = IsDimensionCombined(label)
   180  	}
   181  	if name != "" {
   182  		publish(name, t)
   183  	}
   184  
   185  	return t
   186  }
   187  
   188  // Labels returns the list of labels.
   189  func (mc *CountersWithMultiLabels) Labels() []string {
   190  	return mc.labels
   191  }
   192  
   193  // Add adds a value to a named counter.
   194  // len(names) must be equal to len(Labels)
   195  func (mc *CountersWithMultiLabels) Add(names []string, value int64) {
   196  	if len(names) != len(mc.labels) {
   197  		panic("CountersWithMultiLabels: wrong number of values in Add")
   198  	}
   199  	mc.counters.add(safeJoinLabels(names, mc.combinedLabels), value)
   200  }
   201  
   202  // Reset resets the value of a named counter back to 0.
   203  // len(names) must be equal to len(Labels).
   204  func (mc *CountersWithMultiLabels) Reset(names []string) {
   205  	if len(names) != len(mc.labels) {
   206  		panic("CountersWithMultiLabels: wrong number of values in Reset")
   207  	}
   208  
   209  	mc.counters.set(safeJoinLabels(names, mc.combinedLabels), 0)
   210  }
   211  
   212  // ResetAll clears the counters
   213  func (mc *CountersWithMultiLabels) ResetAll() {
   214  	mc.counters.reset()
   215  }
   216  
   217  // Counts returns a copy of the Counters' map.
   218  // The key is a single string where all labels are joined by a "." e.g.
   219  // "label1.label2".
   220  func (mc *CountersWithMultiLabels) Counts() map[string]int64 {
   221  	return mc.counters.Counts()
   222  }
   223  
   224  // CountersFuncWithMultiLabels is a multidimensional counters implementation
   225  // where names of categories are compound names made with joining
   226  // multiple strings with '.'.  Since the map is returned by the
   227  // function, we assume it's in the right format (meaning each key is
   228  // of the form 'aaa.bbb.ccc' with as many elements as there are in
   229  // Labels).
   230  //
   231  // Note that there is no CountersFuncWithSingleLabel object. That this
   232  // because such an object would be identical to this one because these
   233  // function-based counters have no Add() or Set() method which are different
   234  // for the single vs. multiple labels cases.
   235  // If you have only a single label, pass an array with a single element.
   236  type CountersFuncWithMultiLabels struct {
   237  	f      func() map[string]int64
   238  	help   string
   239  	labels []string
   240  }
   241  
   242  // Labels returns the list of labels.
   243  func (c CountersFuncWithMultiLabels) Labels() []string {
   244  	return c.labels
   245  }
   246  
   247  // Help returns the help string.
   248  func (c CountersFuncWithMultiLabels) Help() string {
   249  	return c.help
   250  }
   251  
   252  // NewCountersFuncWithMultiLabels creates a new CountersFuncWithMultiLabels
   253  // mapping to the provided function.
   254  func NewCountersFuncWithMultiLabels(name, help string, labels []string, f func() map[string]int64) *CountersFuncWithMultiLabels {
   255  	t := &CountersFuncWithMultiLabels{
   256  		f:      f,
   257  		help:   help,
   258  		labels: labels,
   259  	}
   260  	if name != "" {
   261  		publish(name, t)
   262  	}
   263  
   264  	return t
   265  }
   266  
   267  // Counts returns a copy of the counters' map.
   268  func (c CountersFuncWithMultiLabels) Counts() map[string]int64 {
   269  	return c.f()
   270  }
   271  
   272  // String implements the expvar.Var interface.
   273  func (c CountersFuncWithMultiLabels) String() string {
   274  	m := c.f()
   275  	if m == nil {
   276  		return "{}"
   277  	}
   278  	b := bytes.NewBuffer(make([]byte, 0, 4096))
   279  	fmt.Fprintf(b, "{")
   280  	firstValue := true
   281  	for k, v := range m {
   282  		if firstValue {
   283  			firstValue = false
   284  		} else {
   285  			fmt.Fprintf(b, ", ")
   286  		}
   287  		fmt.Fprintf(b, "%q: %v", k, v)
   288  	}
   289  	fmt.Fprintf(b, "}")
   290  	return b.String()
   291  }
   292  
   293  // GaugesWithSingleLabel is similar to CountersWithSingleLabel, except its
   294  // meant to track the current value and not a cumulative count.
   295  type GaugesWithSingleLabel struct {
   296  	CountersWithSingleLabel
   297  }
   298  
   299  // NewGaugesWithSingleLabel creates a new GaugesWithSingleLabel and
   300  // publishes it if the name is set.
   301  func NewGaugesWithSingleLabel(name, help, label string, tags ...string) *GaugesWithSingleLabel {
   302  	g := &GaugesWithSingleLabel{
   303  		CountersWithSingleLabel: CountersWithSingleLabel{
   304  			counters: counters{
   305  				counts: make(map[string]int64),
   306  				help:   help,
   307  			},
   308  			label: label,
   309  		},
   310  	}
   311  
   312  	for _, tag := range tags {
   313  		g.counts[tag] = 0
   314  	}
   315  	if name != "" {
   316  		publish(name, g)
   317  	}
   318  	return g
   319  }
   320  
   321  // Set sets the value of a named gauge.
   322  func (g *GaugesWithSingleLabel) Set(name string, value int64) {
   323  	g.counters.set(name, value)
   324  }
   325  
   326  // GaugesWithMultiLabels is a CountersWithMultiLabels implementation where
   327  // the values can go up and down.
   328  type GaugesWithMultiLabels struct {
   329  	CountersWithMultiLabels
   330  }
   331  
   332  // NewGaugesWithMultiLabels creates a new GaugesWithMultiLabels instance,
   333  // and publishes it if name is set.
   334  func NewGaugesWithMultiLabels(name, help string, labels []string) *GaugesWithMultiLabels {
   335  	t := &GaugesWithMultiLabels{
   336  		CountersWithMultiLabels: CountersWithMultiLabels{
   337  			counters: counters{
   338  				counts: make(map[string]int64),
   339  				help:   help,
   340  			},
   341  			labels: labels,
   342  		}}
   343  	if name != "" {
   344  		publish(name, t)
   345  	}
   346  
   347  	return t
   348  }
   349  
   350  // Set sets the value of a named counter.
   351  // len(names) must be equal to len(Labels).
   352  func (mg *GaugesWithMultiLabels) Set(names []string, value int64) {
   353  	if len(names) != len(mg.CountersWithMultiLabels.labels) {
   354  		panic("GaugesWithMultiLabels: wrong number of values in Set")
   355  	}
   356  	mg.counters.set(safeJoinLabels(names, nil), value)
   357  }
   358  
   359  // GaugesFuncWithMultiLabels is a wrapper around CountersFuncWithMultiLabels
   360  // for values that go up/down for implementations (like Prometheus) that
   361  // need to differ between Counters and Gauges.
   362  type GaugesFuncWithMultiLabels struct {
   363  	CountersFuncWithMultiLabels
   364  }
   365  
   366  // NewGaugesFuncWithMultiLabels creates a new GaugesFuncWithMultiLabels
   367  // mapping to the provided function.
   368  func NewGaugesFuncWithMultiLabels(name, help string, labels []string, f func() map[string]int64) *GaugesFuncWithMultiLabels {
   369  	t := &GaugesFuncWithMultiLabels{
   370  		CountersFuncWithMultiLabels: CountersFuncWithMultiLabels{
   371  			f:      f,
   372  			help:   help,
   373  			labels: labels,
   374  		}}
   375  
   376  	if name != "" {
   377  		publish(name, t)
   378  	}
   379  
   380  	return t
   381  }