237 lines
		
	
	
		
			7.5 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			237 lines
		
	
	
		
			7.5 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
/*
 | 
						|
Copyright 2019 The Kubernetes Authors.
 | 
						|
 | 
						|
Licensed under the Apache License, Version 2.0 (the "License");
 | 
						|
you may not use this file except in compliance with the License.
 | 
						|
You may obtain a copy of the License at
 | 
						|
 | 
						|
    http://www.apache.org/licenses/LICENSE-2.0
 | 
						|
 | 
						|
Unless required by applicable law or agreed to in writing, software
 | 
						|
distributed under the License is distributed on an "AS IS" BASIS,
 | 
						|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
						|
See the License for the specific language governing permissions and
 | 
						|
limitations under the License.
 | 
						|
*/
 | 
						|
 | 
						|
package metrics
 | 
						|
 | 
						|
import (
 | 
						|
	"sync"
 | 
						|
 | 
						|
	"github.com/blang/semver/v4"
 | 
						|
	"github.com/prometheus/client_golang/prometheus"
 | 
						|
	dto "github.com/prometheus/client_model/go"
 | 
						|
	promext "k8s.io/component-base/metrics/prometheusextension"
 | 
						|
 | 
						|
	"k8s.io/klog/v2"
 | 
						|
)
 | 
						|
 | 
						|
/*
 | 
						|
kubeCollector extends the prometheus.Collector interface to allow customization of the metric
 | 
						|
registration process. Defer metric initialization until Create() is called, which then
 | 
						|
delegates to the underlying metric's initializeMetric or initializeDeprecatedMetric
 | 
						|
method call depending on whether the metric is deprecated or not.
 | 
						|
*/
 | 
						|
type kubeCollector interface {
 | 
						|
	Collector
 | 
						|
	lazyKubeMetric
 | 
						|
	DeprecatedVersion() *semver.Version
 | 
						|
	// Each collector metric should provide an initialization function
 | 
						|
	// for both deprecated and non-deprecated variants of a metric. This
 | 
						|
	// is necessary since metric instantiation will be deferred
 | 
						|
	// until the metric is actually registered somewhere.
 | 
						|
	initializeMetric()
 | 
						|
	initializeDeprecatedMetric()
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
lazyKubeMetric defines our metric registration interface. lazyKubeMetric objects are expected
 | 
						|
to lazily instantiate metrics (i.e defer metric instantiation until when
 | 
						|
the Create() function is explicitly called).
 | 
						|
*/
 | 
						|
type lazyKubeMetric interface {
 | 
						|
	Create(*semver.Version) bool
 | 
						|
	IsCreated() bool
 | 
						|
	IsHidden() bool
 | 
						|
	IsDeprecated() bool
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
lazyMetric implements lazyKubeMetric. A lazy metric is lazy because it waits until metric
 | 
						|
registration time before instantiation. Add it as an anonymous field to a struct that
 | 
						|
implements kubeCollector to get deferred registration behavior. You must call lazyInit
 | 
						|
with the kubeCollector itself as an argument.
 | 
						|
*/
 | 
						|
type lazyMetric struct {
 | 
						|
	fqName              string
 | 
						|
	isDeprecated        bool
 | 
						|
	isHidden            bool
 | 
						|
	isCreated           bool
 | 
						|
	createLock          sync.RWMutex
 | 
						|
	markDeprecationOnce sync.Once
 | 
						|
	createOnce          sync.Once
 | 
						|
	self                kubeCollector
 | 
						|
	stabilityLevel      StabilityLevel
 | 
						|
}
 | 
						|
 | 
						|
func (r *lazyMetric) IsCreated() bool {
 | 
						|
	r.createLock.RLock()
 | 
						|
	defer r.createLock.RUnlock()
 | 
						|
	return r.isCreated
 | 
						|
}
 | 
						|
 | 
						|
// lazyInit provides the lazyMetric with a reference to the kubeCollector it is supposed
 | 
						|
// to allow lazy initialization for. It should be invoked in the factory function which creates new
 | 
						|
// kubeCollector type objects.
 | 
						|
func (r *lazyMetric) lazyInit(self kubeCollector, fqName string) {
 | 
						|
	r.fqName = fqName
 | 
						|
	r.self = self
 | 
						|
}
 | 
						|
 | 
						|
// preprocessMetric figures out whether the lazy metric should be hidden or not.
 | 
						|
// This method takes a Version argument which should be the version of the binary in which
 | 
						|
// this code is currently being executed. A metric can be hidden under two conditions:
 | 
						|
//  1. if the metric is deprecated and is outside the grace period (i.e. has been
 | 
						|
//     deprecated for more than one release
 | 
						|
//  2. if the metric is manually disabled via a CLI flag.
 | 
						|
//
 | 
						|
// Disclaimer:  disabling a metric via a CLI flag has higher precedence than
 | 
						|
//
 | 
						|
//	  	deprecation and will override show-hidden-metrics for the explicitly
 | 
						|
//		disabled metric.
 | 
						|
func (r *lazyMetric) preprocessMetric(version semver.Version) {
 | 
						|
	disabledMetricsLock.RLock()
 | 
						|
	defer disabledMetricsLock.RUnlock()
 | 
						|
	// disabling metrics is higher in precedence than showing hidden metrics
 | 
						|
	if _, ok := disabledMetrics[r.fqName]; ok {
 | 
						|
		r.isHidden = true
 | 
						|
		return
 | 
						|
	}
 | 
						|
	selfVersion := r.self.DeprecatedVersion()
 | 
						|
	if selfVersion == nil {
 | 
						|
		return
 | 
						|
	}
 | 
						|
	r.markDeprecationOnce.Do(func() {
 | 
						|
		if selfVersion.LTE(version) {
 | 
						|
			r.isDeprecated = true
 | 
						|
		}
 | 
						|
 | 
						|
		if ShouldShowHidden() {
 | 
						|
			klog.Warningf("Hidden metrics (%s) have been manually overridden, showing this very deprecated metric.", r.fqName)
 | 
						|
			return
 | 
						|
		}
 | 
						|
		if shouldHide(&version, selfVersion) {
 | 
						|
			// TODO(RainbowMango): Remove this log temporarily. https://github.com/kubernetes/kubernetes/issues/85369
 | 
						|
			// klog.Warningf("This metric has been deprecated for more than one release, hiding.")
 | 
						|
			r.isHidden = true
 | 
						|
		}
 | 
						|
	})
 | 
						|
}
 | 
						|
 | 
						|
func (r *lazyMetric) IsHidden() bool {
 | 
						|
	return r.isHidden
 | 
						|
}
 | 
						|
 | 
						|
func (r *lazyMetric) IsDeprecated() bool {
 | 
						|
	return r.isDeprecated
 | 
						|
}
 | 
						|
 | 
						|
// Create forces the initialization of metric which has been deferred until
 | 
						|
// the point at which this method is invoked. This method will determine whether
 | 
						|
// the metric is deprecated or hidden, no-opting if the metric should be considered
 | 
						|
// hidden. Furthermore, this function no-opts and returns true if metric is already
 | 
						|
// created.
 | 
						|
func (r *lazyMetric) Create(version *semver.Version) bool {
 | 
						|
	if version != nil {
 | 
						|
		r.preprocessMetric(*version)
 | 
						|
	}
 | 
						|
	// let's not create if this metric is slated to be hidden
 | 
						|
	if r.IsHidden() {
 | 
						|
		return false
 | 
						|
	}
 | 
						|
 | 
						|
	r.createOnce.Do(func() {
 | 
						|
		r.createLock.Lock()
 | 
						|
		defer r.createLock.Unlock()
 | 
						|
		r.isCreated = true
 | 
						|
		if r.IsDeprecated() {
 | 
						|
			r.self.initializeDeprecatedMetric()
 | 
						|
		} else {
 | 
						|
			r.self.initializeMetric()
 | 
						|
		}
 | 
						|
	})
 | 
						|
	sl := r.stabilityLevel
 | 
						|
	deprecatedV := r.self.DeprecatedVersion()
 | 
						|
	dv := ""
 | 
						|
	if deprecatedV != nil {
 | 
						|
		dv = deprecatedV.String()
 | 
						|
	}
 | 
						|
	registeredMetrics.WithLabelValues(string(sl), dv).Inc()
 | 
						|
	return r.IsCreated()
 | 
						|
}
 | 
						|
 | 
						|
// ClearState will clear all the states marked by Create.
 | 
						|
// It intends to be used for re-register a hidden metric.
 | 
						|
func (r *lazyMetric) ClearState() {
 | 
						|
	r.createLock.Lock()
 | 
						|
	defer r.createLock.Unlock()
 | 
						|
 | 
						|
	r.isDeprecated = false
 | 
						|
	r.isHidden = false
 | 
						|
	r.isCreated = false
 | 
						|
	r.markDeprecationOnce = sync.Once{}
 | 
						|
	r.createOnce = sync.Once{}
 | 
						|
}
 | 
						|
 | 
						|
// FQName returns the fully-qualified metric name of the collector.
 | 
						|
func (r *lazyMetric) FQName() string {
 | 
						|
	return r.fqName
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
This code is directly lifted from the prometheus codebase. It's a convenience struct which
 | 
						|
allows you satisfy the Collector interface automatically if you already satisfy the Metric interface.
 | 
						|
 | 
						|
For reference: https://github.com/prometheus/client_golang/blob/v0.9.2/prometheus/collector.go#L98-L120
 | 
						|
*/
 | 
						|
type selfCollector struct {
 | 
						|
	metric prometheus.Metric
 | 
						|
}
 | 
						|
 | 
						|
func (c *selfCollector) initSelfCollection(m prometheus.Metric) {
 | 
						|
	c.metric = m
 | 
						|
}
 | 
						|
 | 
						|
func (c *selfCollector) Describe(ch chan<- *prometheus.Desc) {
 | 
						|
	ch <- c.metric.Desc()
 | 
						|
}
 | 
						|
 | 
						|
func (c *selfCollector) Collect(ch chan<- prometheus.Metric) {
 | 
						|
	ch <- c.metric
 | 
						|
}
 | 
						|
 | 
						|
// no-op vecs for convenience
 | 
						|
var noopCounterVec = &prometheus.CounterVec{}
 | 
						|
var noopHistogramVec = &prometheus.HistogramVec{}
 | 
						|
var noopTimingHistogramVec = &promext.TimingHistogramVec{}
 | 
						|
var noopGaugeVec = &prometheus.GaugeVec{}
 | 
						|
 | 
						|
// just use a convenience struct for all the no-ops
 | 
						|
var noop = &noopMetric{}
 | 
						|
 | 
						|
type noopMetric struct{}
 | 
						|
 | 
						|
func (noopMetric) Inc()                              {}
 | 
						|
func (noopMetric) Add(float64)                       {}
 | 
						|
func (noopMetric) Dec()                              {}
 | 
						|
func (noopMetric) Set(float64)                       {}
 | 
						|
func (noopMetric) Sub(float64)                       {}
 | 
						|
func (noopMetric) Observe(float64)                   {}
 | 
						|
func (noopMetric) ObserveWithWeight(float64, uint64) {}
 | 
						|
func (noopMetric) SetToCurrentTime()                 {}
 | 
						|
func (noopMetric) Desc() *prometheus.Desc            { return nil }
 | 
						|
func (noopMetric) Write(*dto.Metric) error           { return nil }
 | 
						|
func (noopMetric) Describe(chan<- *prometheus.Desc)  {}
 | 
						|
func (noopMetric) Collect(chan<- prometheus.Metric)  {}
 |