...

Source file src/k8s.io/component-base/metrics/gauge.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  
    25  	"k8s.io/component-base/version"
    26  )
    27  
    28  // Gauge is our internal representation for our wrapping struct around prometheus
    29  // gauges. kubeGauge implements both kubeCollector and KubeGauge.
    30  type Gauge struct {
    31  	GaugeMetric
    32  	*GaugeOpts
    33  	lazyMetric
    34  	selfCollector
    35  }
    36  
    37  var _ GaugeMetric = &Gauge{}
    38  var _ Registerable = &Gauge{}
    39  var _ kubeCollector = &Gauge{}
    40  
    41  // NewGauge returns an object which satisfies the kubeCollector, Registerable, and Gauge interfaces.
    42  // However, the object returned will not measure anything unless the collector is first
    43  // registered, since the metric is lazily instantiated.
    44  func NewGauge(opts *GaugeOpts) *Gauge {
    45  	opts.StabilityLevel.setDefaults()
    46  
    47  	kc := &Gauge{
    48  		GaugeOpts:  opts,
    49  		lazyMetric: lazyMetric{stabilityLevel: opts.StabilityLevel},
    50  	}
    51  	kc.setPrometheusGauge(noop)
    52  	kc.lazyInit(kc, BuildFQName(opts.Namespace, opts.Subsystem, opts.Name))
    53  	return kc
    54  }
    55  
    56  // setPrometheusGauge sets the underlying KubeGauge object, i.e. the thing that does the measurement.
    57  func (g *Gauge) setPrometheusGauge(gauge prometheus.Gauge) {
    58  	g.GaugeMetric = gauge
    59  	g.initSelfCollection(gauge)
    60  }
    61  
    62  // DeprecatedVersion returns a pointer to the Version or nil
    63  func (g *Gauge) DeprecatedVersion() *semver.Version {
    64  	return parseSemver(g.GaugeOpts.DeprecatedVersion)
    65  }
    66  
    67  // initializeMetric invocation creates the actual underlying Gauge. Until this method is called
    68  // the underlying gauge is a no-op.
    69  func (g *Gauge) initializeMetric() {
    70  	g.GaugeOpts.annotateStabilityLevel()
    71  	// this actually creates the underlying prometheus gauge.
    72  	g.setPrometheusGauge(prometheus.NewGauge(g.GaugeOpts.toPromGaugeOpts()))
    73  }
    74  
    75  // initializeDeprecatedMetric invocation creates the actual (but deprecated) Gauge. Until this method
    76  // is called the underlying gauge is a no-op.
    77  func (g *Gauge) initializeDeprecatedMetric() {
    78  	g.GaugeOpts.markDeprecated()
    79  	g.initializeMetric()
    80  }
    81  
    82  // WithContext allows the normal Gauge metric to pass in context. The context is no-op now.
    83  func (g *Gauge) WithContext(ctx context.Context) GaugeMetric {
    84  	return g.GaugeMetric
    85  }
    86  
    87  // GaugeVec is the internal representation of our wrapping struct around prometheus
    88  // gaugeVecs. kubeGaugeVec implements both kubeCollector and KubeGaugeVec.
    89  type GaugeVec struct {
    90  	*prometheus.GaugeVec
    91  	*GaugeOpts
    92  	lazyMetric
    93  	originalLabels []string
    94  }
    95  
    96  var _ GaugeVecMetric = &GaugeVec{}
    97  var _ Registerable = &GaugeVec{}
    98  var _ kubeCollector = &GaugeVec{}
    99  
   100  // NewGaugeVec returns an object which satisfies the kubeCollector, Registerable, and GaugeVecMetric interfaces.
   101  // However, the object returned will not measure anything unless the collector is first
   102  // registered, since the metric is lazily instantiated, and only members extracted after
   103  // registration will actually measure anything.
   104  func NewGaugeVec(opts *GaugeOpts, labels []string) *GaugeVec {
   105  	opts.StabilityLevel.setDefaults()
   106  
   107  	fqName := BuildFQName(opts.Namespace, opts.Subsystem, opts.Name)
   108  	allowListLock.RLock()
   109  	if allowList, ok := labelValueAllowLists[fqName]; ok {
   110  		opts.LabelValueAllowLists = allowList
   111  	}
   112  	allowListLock.RUnlock()
   113  
   114  	cv := &GaugeVec{
   115  		GaugeVec:       noopGaugeVec,
   116  		GaugeOpts:      opts,
   117  		originalLabels: labels,
   118  		lazyMetric:     lazyMetric{stabilityLevel: opts.StabilityLevel},
   119  	}
   120  	cv.lazyInit(cv, fqName)
   121  	return cv
   122  }
   123  
   124  // DeprecatedVersion returns a pointer to the Version or nil
   125  func (v *GaugeVec) DeprecatedVersion() *semver.Version {
   126  	return parseSemver(v.GaugeOpts.DeprecatedVersion)
   127  }
   128  
   129  // initializeMetric invocation creates the actual underlying GaugeVec. Until this method is called
   130  // the underlying gaugeVec is a no-op.
   131  func (v *GaugeVec) initializeMetric() {
   132  	v.GaugeOpts.annotateStabilityLevel()
   133  	v.GaugeVec = prometheus.NewGaugeVec(v.GaugeOpts.toPromGaugeOpts(), v.originalLabels)
   134  }
   135  
   136  // initializeDeprecatedMetric invocation creates the actual (but deprecated) GaugeVec. Until this method is called
   137  // the underlying gaugeVec is a no-op.
   138  func (v *GaugeVec) initializeDeprecatedMetric() {
   139  	v.GaugeOpts.markDeprecated()
   140  	v.initializeMetric()
   141  }
   142  
   143  func (v *GaugeVec) WithLabelValuesChecked(lvs ...string) (GaugeMetric, error) {
   144  	if !v.IsCreated() {
   145  		if v.IsHidden() {
   146  			return noop, nil
   147  		}
   148  		return noop, errNotRegistered // return no-op gauge
   149  	}
   150  	if v.LabelValueAllowLists != nil {
   151  		v.LabelValueAllowLists.ConstrainToAllowedList(v.originalLabels, lvs)
   152  	}
   153  	elt, err := v.GaugeVec.GetMetricWithLabelValues(lvs...)
   154  	return elt, err
   155  }
   156  
   157  // Default Prometheus Vec behavior is that member extraction results in creation of a new element
   158  // if one with the unique label values is not found in the underlying stored metricMap.
   159  // This means  that if this function is called but the underlying metric is not registered
   160  // (which means it will never be exposed externally nor consumed), the metric will exist in memory
   161  // for perpetuity (i.e. throughout application lifecycle).
   162  //
   163  // For reference: https://github.com/prometheus/client_golang/blob/v0.9.2/prometheus/gauge.go#L190-L208
   164  //
   165  // In contrast, the Vec behavior in this package is that member extraction before registration
   166  // returns a permanent noop object.
   167  
   168  // WithLabelValues returns the GaugeMetric for the given slice of label
   169  // values (same order as the VariableLabels in Desc). If that combination of
   170  // label values is accessed for the first time, a new GaugeMetric is created IFF the gaugeVec
   171  // has been registered to a metrics registry.
   172  func (v *GaugeVec) WithLabelValues(lvs ...string) GaugeMetric {
   173  	ans, err := v.WithLabelValuesChecked(lvs...)
   174  	if err == nil || ErrIsNotRegistered(err) {
   175  		return ans
   176  	}
   177  	panic(err)
   178  }
   179  
   180  func (v *GaugeVec) WithChecked(labels map[string]string) (GaugeMetric, error) {
   181  	if !v.IsCreated() {
   182  		if v.IsHidden() {
   183  			return noop, nil
   184  		}
   185  		return noop, errNotRegistered // return no-op gauge
   186  	}
   187  	if v.LabelValueAllowLists != nil {
   188  		v.LabelValueAllowLists.ConstrainLabelMap(labels)
   189  	}
   190  	elt, err := v.GaugeVec.GetMetricWith(labels)
   191  	return elt, err
   192  }
   193  
   194  // With returns the GaugeMetric for the given Labels map (the label names
   195  // must match those of the VariableLabels in Desc). If that label map is
   196  // accessed for the first time, a new GaugeMetric is created IFF the gaugeVec has
   197  // been registered to a metrics registry.
   198  func (v *GaugeVec) With(labels map[string]string) GaugeMetric {
   199  	ans, err := v.WithChecked(labels)
   200  	if err == nil || ErrIsNotRegistered(err) {
   201  		return ans
   202  	}
   203  	panic(err)
   204  }
   205  
   206  // Delete deletes the metric where the variable labels are the same as those
   207  // passed in as labels. It returns true if a metric was deleted.
   208  //
   209  // It is not an error if the number and names of the Labels are inconsistent
   210  // with those of the VariableLabels in Desc. However, such inconsistent Labels
   211  // can never match an actual metric, so the method will always return false in
   212  // that case.
   213  func (v *GaugeVec) Delete(labels map[string]string) bool {
   214  	if !v.IsCreated() {
   215  		return false // since we haven't created the metric, we haven't deleted a metric with the passed in values
   216  	}
   217  	return v.GaugeVec.Delete(labels)
   218  }
   219  
   220  // Reset deletes all metrics in this vector.
   221  func (v *GaugeVec) Reset() {
   222  	if !v.IsCreated() {
   223  		return
   224  	}
   225  
   226  	v.GaugeVec.Reset()
   227  }
   228  
   229  func newGaugeFunc(opts *GaugeOpts, function func() float64, v semver.Version) GaugeFunc {
   230  	g := NewGauge(opts)
   231  
   232  	if !g.Create(&v) {
   233  		return nil
   234  	}
   235  
   236  	return prometheus.NewGaugeFunc(g.GaugeOpts.toPromGaugeOpts(), function)
   237  }
   238  
   239  // NewGaugeFunc creates a new GaugeFunc based on the provided GaugeOpts. The
   240  // value reported is determined by calling the given function from within the
   241  // Write method. Take into account that metric collection may happen
   242  // concurrently. If that results in concurrent calls to Write, like in the case
   243  // where a GaugeFunc is directly registered with Prometheus, the provided
   244  // function must be concurrency-safe.
   245  func NewGaugeFunc(opts *GaugeOpts, function func() float64) GaugeFunc {
   246  	v := parseVersion(version.Get())
   247  
   248  	return newGaugeFunc(opts, function, v)
   249  }
   250  
   251  // WithContext returns wrapped GaugeVec with context
   252  func (v *GaugeVec) WithContext(ctx context.Context) *GaugeVecWithContext {
   253  	return &GaugeVecWithContext{
   254  		ctx:      ctx,
   255  		GaugeVec: v,
   256  	}
   257  }
   258  
   259  func (v *GaugeVec) InterfaceWithContext(ctx context.Context) GaugeVecMetric {
   260  	return v.WithContext(ctx)
   261  }
   262  
   263  // GaugeVecWithContext is the wrapper of GaugeVec with context.
   264  type GaugeVecWithContext struct {
   265  	*GaugeVec
   266  	ctx context.Context
   267  }
   268  
   269  // WithLabelValues is the wrapper of GaugeVec.WithLabelValues.
   270  func (vc *GaugeVecWithContext) WithLabelValues(lvs ...string) GaugeMetric {
   271  	return vc.GaugeVec.WithLabelValues(lvs...)
   272  }
   273  
   274  // With is the wrapper of GaugeVec.With.
   275  func (vc *GaugeVecWithContext) With(labels map[string]string) GaugeMetric {
   276  	return vc.GaugeVec.With(labels)
   277  }
   278  

View as plain text