...

Source file src/k8s.io/component-base/metrics/counter.go

Documentation: k8s.io/component-base/metrics

     1  /*
     2  Copyright 2019 The Kubernetes 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 metrics
    18  
    19  import (
    20  	"context"
    21  
    22  	"github.com/blang/semver/v4"
    23  	"github.com/prometheus/client_golang/prometheus"
    24  	dto "github.com/prometheus/client_model/go"
    25  )
    26  
    27  // Counter is our internal representation for our wrapping struct around prometheus
    28  // counters. Counter implements both kubeCollector and CounterMetric.
    29  type Counter struct {
    30  	CounterMetric
    31  	*CounterOpts
    32  	lazyMetric
    33  	selfCollector
    34  }
    35  
    36  // The implementation of the Metric interface is expected by testutil.GetCounterMetricValue.
    37  var _ Metric = &Counter{}
    38  
    39  // NewCounter returns an object which satisfies the kubeCollector and CounterMetric interfaces.
    40  // However, the object returned will not measure anything unless the collector is first
    41  // registered, since the metric is lazily instantiated.
    42  func NewCounter(opts *CounterOpts) *Counter {
    43  	opts.StabilityLevel.setDefaults()
    44  
    45  	kc := &Counter{
    46  		CounterOpts: opts,
    47  		lazyMetric:  lazyMetric{stabilityLevel: opts.StabilityLevel},
    48  	}
    49  	kc.setPrometheusCounter(noop)
    50  	kc.lazyInit(kc, BuildFQName(opts.Namespace, opts.Subsystem, opts.Name))
    51  	return kc
    52  }
    53  
    54  func (c *Counter) Desc() *prometheus.Desc {
    55  	return c.metric.Desc()
    56  }
    57  
    58  func (c *Counter) Write(to *dto.Metric) error {
    59  	return c.metric.Write(to)
    60  }
    61  
    62  // Reset resets the underlying prometheus Counter to start counting from 0 again
    63  func (c *Counter) Reset() {
    64  	if !c.IsCreated() {
    65  		return
    66  	}
    67  	c.setPrometheusCounter(prometheus.NewCounter(c.CounterOpts.toPromCounterOpts()))
    68  }
    69  
    70  // setPrometheusCounter sets the underlying CounterMetric object, i.e. the thing that does the measurement.
    71  func (c *Counter) setPrometheusCounter(counter prometheus.Counter) {
    72  	c.CounterMetric = counter
    73  	c.initSelfCollection(counter)
    74  }
    75  
    76  // DeprecatedVersion returns a pointer to the Version or nil
    77  func (c *Counter) DeprecatedVersion() *semver.Version {
    78  	return parseSemver(c.CounterOpts.DeprecatedVersion)
    79  }
    80  
    81  // initializeMetric invocation creates the actual underlying Counter. Until this method is called
    82  // the underlying counter is a no-op.
    83  func (c *Counter) initializeMetric() {
    84  	c.CounterOpts.annotateStabilityLevel()
    85  	// this actually creates the underlying prometheus counter.
    86  	c.setPrometheusCounter(prometheus.NewCounter(c.CounterOpts.toPromCounterOpts()))
    87  }
    88  
    89  // initializeDeprecatedMetric invocation creates the actual (but deprecated) Counter. Until this method
    90  // is called the underlying counter is a no-op.
    91  func (c *Counter) initializeDeprecatedMetric() {
    92  	c.CounterOpts.markDeprecated()
    93  	c.initializeMetric()
    94  }
    95  
    96  // WithContext allows the normal Counter metric to pass in context. The context is no-op now.
    97  func (c *Counter) WithContext(ctx context.Context) CounterMetric {
    98  	return c.CounterMetric
    99  }
   100  
   101  // CounterVec is the internal representation of our wrapping struct around prometheus
   102  // counterVecs. CounterVec implements both kubeCollector and CounterVecMetric.
   103  type CounterVec struct {
   104  	*prometheus.CounterVec
   105  	*CounterOpts
   106  	lazyMetric
   107  	originalLabels []string
   108  }
   109  
   110  var _ kubeCollector = &CounterVec{}
   111  
   112  // TODO: make this true: var _ CounterVecMetric = &CounterVec{}
   113  
   114  // NewCounterVec returns an object which satisfies the kubeCollector and (almost) CounterVecMetric interfaces.
   115  // However, the object returned will not measure anything unless the collector is first
   116  // registered, since the metric is lazily instantiated, and only members extracted after
   117  // registration will actually measure anything.
   118  func NewCounterVec(opts *CounterOpts, labels []string) *CounterVec {
   119  	opts.StabilityLevel.setDefaults()
   120  
   121  	fqName := BuildFQName(opts.Namespace, opts.Subsystem, opts.Name)
   122  	allowListLock.RLock()
   123  	if allowList, ok := labelValueAllowLists[fqName]; ok {
   124  		opts.LabelValueAllowLists = allowList
   125  	}
   126  	allowListLock.RUnlock()
   127  
   128  	cv := &CounterVec{
   129  		CounterVec:     noopCounterVec,
   130  		CounterOpts:    opts,
   131  		originalLabels: labels,
   132  		lazyMetric:     lazyMetric{stabilityLevel: opts.StabilityLevel},
   133  	}
   134  	cv.lazyInit(cv, fqName)
   135  	return cv
   136  }
   137  
   138  // DeprecatedVersion returns a pointer to the Version or nil
   139  func (v *CounterVec) DeprecatedVersion() *semver.Version {
   140  	return parseSemver(v.CounterOpts.DeprecatedVersion)
   141  
   142  }
   143  
   144  // initializeMetric invocation creates the actual underlying CounterVec. Until this method is called
   145  // the underlying counterVec is a no-op.
   146  func (v *CounterVec) initializeMetric() {
   147  	v.CounterOpts.annotateStabilityLevel()
   148  	v.CounterVec = prometheus.NewCounterVec(v.CounterOpts.toPromCounterOpts(), v.originalLabels)
   149  }
   150  
   151  // initializeDeprecatedMetric invocation creates the actual (but deprecated) CounterVec. Until this method is called
   152  // the underlying counterVec is a no-op.
   153  func (v *CounterVec) initializeDeprecatedMetric() {
   154  	v.CounterOpts.markDeprecated()
   155  	v.initializeMetric()
   156  }
   157  
   158  // Default Prometheus Vec behavior is that member extraction results in creation of a new element
   159  // if one with the unique label values is not found in the underlying stored metricMap.
   160  // This means  that if this function is called but the underlying metric is not registered
   161  // (which means it will never be exposed externally nor consumed), the metric will exist in memory
   162  // for perpetuity (i.e. throughout application lifecycle).
   163  //
   164  // For reference: https://github.com/prometheus/client_golang/blob/v0.9.2/prometheus/counter.go#L179-L197
   165  //
   166  // In contrast, the Vec behavior in this package is that member extraction before registration
   167  // returns a permanent noop object.
   168  
   169  // WithLabelValues returns the Counter for the given slice of label
   170  // values (same order as the VariableLabels in Desc). If that combination of
   171  // label values is accessed for the first time, a new Counter is created IFF the counterVec
   172  // has been registered to a metrics registry.
   173  func (v *CounterVec) WithLabelValues(lvs ...string) CounterMetric {
   174  	if !v.IsCreated() {
   175  		return noop // return no-op counter
   176  	}
   177  	if v.LabelValueAllowLists != nil {
   178  		v.LabelValueAllowLists.ConstrainToAllowedList(v.originalLabels, lvs)
   179  	}
   180  	return v.CounterVec.WithLabelValues(lvs...)
   181  }
   182  
   183  // With returns the Counter for the given Labels map (the label names
   184  // must match those of the VariableLabels in Desc). If that label map is
   185  // accessed for the first time, a new Counter is created IFF the counterVec has
   186  // been registered to a metrics registry.
   187  func (v *CounterVec) With(labels map[string]string) CounterMetric {
   188  	if !v.IsCreated() {
   189  		return noop // return no-op counter
   190  	}
   191  	if v.LabelValueAllowLists != nil {
   192  		v.LabelValueAllowLists.ConstrainLabelMap(labels)
   193  	}
   194  	return v.CounterVec.With(labels)
   195  }
   196  
   197  // Delete deletes the metric where the variable labels are the same as those
   198  // passed in as labels. It returns true if a metric was deleted.
   199  //
   200  // It is not an error if the number and names of the Labels are inconsistent
   201  // with those of the VariableLabels in Desc. However, such inconsistent Labels
   202  // can never match an actual metric, so the method will always return false in
   203  // that case.
   204  func (v *CounterVec) Delete(labels map[string]string) bool {
   205  	if !v.IsCreated() {
   206  		return false // since we haven't created the metric, we haven't deleted a metric with the passed in values
   207  	}
   208  	return v.CounterVec.Delete(labels)
   209  }
   210  
   211  // Reset deletes all metrics in this vector.
   212  func (v *CounterVec) Reset() {
   213  	if !v.IsCreated() {
   214  		return
   215  	}
   216  
   217  	v.CounterVec.Reset()
   218  }
   219  
   220  // WithContext returns wrapped CounterVec with context
   221  func (v *CounterVec) WithContext(ctx context.Context) *CounterVecWithContext {
   222  	return &CounterVecWithContext{
   223  		ctx:        ctx,
   224  		CounterVec: v,
   225  	}
   226  }
   227  
   228  // CounterVecWithContext is the wrapper of CounterVec with context.
   229  type CounterVecWithContext struct {
   230  	*CounterVec
   231  	ctx context.Context
   232  }
   233  
   234  // WithLabelValues is the wrapper of CounterVec.WithLabelValues.
   235  func (vc *CounterVecWithContext) WithLabelValues(lvs ...string) CounterMetric {
   236  	return vc.CounterVec.WithLabelValues(lvs...)
   237  }
   238  
   239  // With is the wrapper of CounterVec.With.
   240  func (vc *CounterVecWithContext) With(labels map[string]string) CounterMetric {
   241  	return vc.CounterVec.With(labels)
   242  }
   243  

View as plain text