...

Source file src/k8s.io/component-base/metrics/metric.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  	"sync"
    21  
    22  	"github.com/blang/semver/v4"
    23  	"github.com/prometheus/client_golang/prometheus"
    24  	dto "github.com/prometheus/client_model/go"
    25  	promext "k8s.io/component-base/metrics/prometheusextension"
    26  
    27  	"k8s.io/klog/v2"
    28  )
    29  
    30  /*
    31  kubeCollector extends the prometheus.Collector interface to allow customization of the metric
    32  registration process. Defer metric initialization until Create() is called, which then
    33  delegates to the underlying metric's initializeMetric or initializeDeprecatedMetric
    34  method call depending on whether the metric is deprecated or not.
    35  */
    36  type kubeCollector interface {
    37  	Collector
    38  	lazyKubeMetric
    39  	DeprecatedVersion() *semver.Version
    40  	// Each collector metric should provide an initialization function
    41  	// for both deprecated and non-deprecated variants of a metric. This
    42  	// is necessary since metric instantiation will be deferred
    43  	// until the metric is actually registered somewhere.
    44  	initializeMetric()
    45  	initializeDeprecatedMetric()
    46  }
    47  
    48  /*
    49  lazyKubeMetric defines our metric registration interface. lazyKubeMetric objects are expected
    50  to lazily instantiate metrics (i.e defer metric instantiation until when
    51  the Create() function is explicitly called).
    52  */
    53  type lazyKubeMetric interface {
    54  	Create(*semver.Version) bool
    55  	IsCreated() bool
    56  	IsHidden() bool
    57  	IsDeprecated() bool
    58  }
    59  
    60  /*
    61  lazyMetric implements lazyKubeMetric. A lazy metric is lazy because it waits until metric
    62  registration time before instantiation. Add it as an anonymous field to a struct that
    63  implements kubeCollector to get deferred registration behavior. You must call lazyInit
    64  with the kubeCollector itself as an argument.
    65  */
    66  type lazyMetric struct {
    67  	fqName              string
    68  	isDeprecated        bool
    69  	isHidden            bool
    70  	isCreated           bool
    71  	createLock          sync.RWMutex
    72  	markDeprecationOnce sync.Once
    73  	createOnce          sync.Once
    74  	self                kubeCollector
    75  	stabilityLevel      StabilityLevel
    76  }
    77  
    78  func (r *lazyMetric) IsCreated() bool {
    79  	r.createLock.RLock()
    80  	defer r.createLock.RUnlock()
    81  	return r.isCreated
    82  }
    83  
    84  // lazyInit provides the lazyMetric with a reference to the kubeCollector it is supposed
    85  // to allow lazy initialization for. It should be invoked in the factory function which creates new
    86  // kubeCollector type objects.
    87  func (r *lazyMetric) lazyInit(self kubeCollector, fqName string) {
    88  	r.fqName = fqName
    89  	r.self = self
    90  }
    91  
    92  // preprocessMetric figures out whether the lazy metric should be hidden or not.
    93  // This method takes a Version argument which should be the version of the binary in which
    94  // this code is currently being executed. A metric can be hidden under two conditions:
    95  //  1. if the metric is deprecated and is outside the grace period (i.e. has been
    96  //     deprecated for more than one release
    97  //  2. if the metric is manually disabled via a CLI flag.
    98  //
    99  // Disclaimer:  disabling a metric via a CLI flag has higher precedence than
   100  // deprecation and will override show-hidden-metrics for the explicitly
   101  // disabled metric.
   102  func (r *lazyMetric) preprocessMetric(version semver.Version) {
   103  	disabledMetricsLock.RLock()
   104  	defer disabledMetricsLock.RUnlock()
   105  	// disabling metrics is higher in precedence than showing hidden metrics
   106  	if _, ok := disabledMetrics[r.fqName]; ok {
   107  		r.isHidden = true
   108  		return
   109  	}
   110  	selfVersion := r.self.DeprecatedVersion()
   111  	if selfVersion == nil {
   112  		return
   113  	}
   114  	r.markDeprecationOnce.Do(func() {
   115  		if selfVersion.LTE(version) {
   116  			r.isDeprecated = true
   117  		}
   118  
   119  		if ShouldShowHidden() {
   120  			klog.Warningf("Hidden metrics (%s) have been manually overridden, showing this very deprecated metric.", r.fqName)
   121  			return
   122  		}
   123  		if shouldHide(&version, selfVersion) {
   124  			// TODO(RainbowMango): Remove this log temporarily. https://github.com/kubernetes/kubernetes/issues/85369
   125  			// klog.Warningf("This metric has been deprecated for more than one release, hiding.")
   126  			r.isHidden = true
   127  		}
   128  	})
   129  }
   130  
   131  func (r *lazyMetric) IsHidden() bool {
   132  	return r.isHidden
   133  }
   134  
   135  func (r *lazyMetric) IsDeprecated() bool {
   136  	return r.isDeprecated
   137  }
   138  
   139  // Create forces the initialization of metric which has been deferred until
   140  // the point at which this method is invoked. This method will determine whether
   141  // the metric is deprecated or hidden, no-opting if the metric should be considered
   142  // hidden. Furthermore, this function no-opts and returns true if metric is already
   143  // created.
   144  func (r *lazyMetric) Create(version *semver.Version) bool {
   145  	if version != nil {
   146  		r.preprocessMetric(*version)
   147  	}
   148  	// let's not create if this metric is slated to be hidden
   149  	if r.IsHidden() {
   150  		return false
   151  	}
   152  
   153  	r.createOnce.Do(func() {
   154  		r.createLock.Lock()
   155  		defer r.createLock.Unlock()
   156  		r.isCreated = true
   157  		if r.IsDeprecated() {
   158  			r.self.initializeDeprecatedMetric()
   159  		} else {
   160  			r.self.initializeMetric()
   161  		}
   162  	})
   163  	sl := r.stabilityLevel
   164  	deprecatedV := r.self.DeprecatedVersion()
   165  	dv := ""
   166  	if deprecatedV != nil {
   167  		dv = deprecatedV.String()
   168  	}
   169  	registeredMetricsTotal.WithLabelValues(string(sl), dv).Inc()
   170  	return r.IsCreated()
   171  }
   172  
   173  // ClearState will clear all the states marked by Create.
   174  // It intends to be used for re-register a hidden metric.
   175  func (r *lazyMetric) ClearState() {
   176  	r.createLock.Lock()
   177  	defer r.createLock.Unlock()
   178  
   179  	r.isDeprecated = false
   180  	r.isHidden = false
   181  	r.isCreated = false
   182  	r.markDeprecationOnce = sync.Once{}
   183  	r.createOnce = sync.Once{}
   184  }
   185  
   186  // FQName returns the fully-qualified metric name of the collector.
   187  func (r *lazyMetric) FQName() string {
   188  	return r.fqName
   189  }
   190  
   191  /*
   192  This code is directly lifted from the prometheus codebase. It's a convenience struct which
   193  allows you satisfy the Collector interface automatically if you already satisfy the Metric interface.
   194  
   195  For reference: https://github.com/prometheus/client_golang/blob/v0.9.2/prometheus/collector.go#L98-L120
   196  */
   197  type selfCollector struct {
   198  	metric prometheus.Metric
   199  }
   200  
   201  func (c *selfCollector) initSelfCollection(m prometheus.Metric) {
   202  	c.metric = m
   203  }
   204  
   205  func (c *selfCollector) Describe(ch chan<- *prometheus.Desc) {
   206  	ch <- c.metric.Desc()
   207  }
   208  
   209  func (c *selfCollector) Collect(ch chan<- prometheus.Metric) {
   210  	ch <- c.metric
   211  }
   212  
   213  // no-op vecs for convenience
   214  var noopCounterVec = &prometheus.CounterVec{}
   215  var noopHistogramVec = &prometheus.HistogramVec{}
   216  var noopTimingHistogramVec = &promext.TimingHistogramVec{}
   217  var noopGaugeVec = &prometheus.GaugeVec{}
   218  
   219  // just use a convenience struct for all the no-ops
   220  var noop = &noopMetric{}
   221  
   222  type noopMetric struct{}
   223  
   224  func (noopMetric) Inc()                              {}
   225  func (noopMetric) Add(float64)                       {}
   226  func (noopMetric) Dec()                              {}
   227  func (noopMetric) Set(float64)                       {}
   228  func (noopMetric) Sub(float64)                       {}
   229  func (noopMetric) Observe(float64)                   {}
   230  func (noopMetric) ObserveWithWeight(float64, uint64) {}
   231  func (noopMetric) SetToCurrentTime()                 {}
   232  func (noopMetric) Desc() *prometheus.Desc            { return nil }
   233  func (noopMetric) Write(*dto.Metric) error           { return nil }
   234  func (noopMetric) Describe(chan<- *prometheus.Desc)  {}
   235  func (noopMetric) Collect(chan<- prometheus.Metric)  {}
   236  

View as plain text