...

Source file src/k8s.io/component-base/metrics/registry.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  	"fmt"
    21  	"sync"
    22  	"sync/atomic"
    23  
    24  	"github.com/blang/semver/v4"
    25  	"github.com/prometheus/client_golang/prometheus"
    26  	dto "github.com/prometheus/client_model/go"
    27  
    28  	apimachineryversion "k8s.io/apimachinery/pkg/version"
    29  	"k8s.io/component-base/version"
    30  )
    31  
    32  var (
    33  	showHiddenOnce      sync.Once
    34  	disabledMetricsLock sync.RWMutex
    35  	showHidden          atomic.Bool
    36  	registries          []*kubeRegistry // stores all registries created by NewKubeRegistry()
    37  	registriesLock      sync.RWMutex
    38  	disabledMetrics     = map[string]struct{}{}
    39  
    40  	registeredMetricsTotal = NewCounterVec(
    41  		&CounterOpts{
    42  			Name:           "registered_metrics_total",
    43  			Help:           "The count of registered metrics broken by stability level and deprecation version.",
    44  			StabilityLevel: BETA,
    45  		},
    46  		[]string{"stability_level", "deprecated_version"},
    47  	)
    48  
    49  	disabledMetricsTotal = NewCounter(
    50  		&CounterOpts{
    51  			Name:           "disabled_metrics_total",
    52  			Help:           "The count of disabled metrics.",
    53  			StabilityLevel: BETA,
    54  		},
    55  	)
    56  
    57  	hiddenMetricsTotal = NewCounter(
    58  		&CounterOpts{
    59  			Name:           "hidden_metrics_total",
    60  			Help:           "The count of hidden metrics.",
    61  			StabilityLevel: BETA,
    62  		},
    63  	)
    64  
    65  	cardinalityEnforcementUnexpectedCategorizationsTotal = NewCounter(
    66  		&CounterOpts{
    67  			Name:           "cardinality_enforcement_unexpected_categorizations_total",
    68  			Help:           "The count of unexpected categorizations during cardinality enforcement.",
    69  			StabilityLevel: ALPHA,
    70  		},
    71  	)
    72  )
    73  
    74  // shouldHide be used to check if a specific metric with deprecated version should be hidden
    75  // according to metrics deprecation lifecycle.
    76  func shouldHide(currentVersion *semver.Version, deprecatedVersion *semver.Version) bool {
    77  	guardVersion, err := semver.Make(fmt.Sprintf("%d.%d.0", currentVersion.Major, currentVersion.Minor))
    78  	if err != nil {
    79  		panic("failed to make version from current version")
    80  	}
    81  
    82  	if deprecatedVersion.LT(guardVersion) {
    83  		return true
    84  	}
    85  
    86  	return false
    87  }
    88  
    89  // ValidateShowHiddenMetricsVersion checks invalid version for which show hidden metrics.
    90  func ValidateShowHiddenMetricsVersion(v string) []error {
    91  	err := validateShowHiddenMetricsVersion(parseVersion(version.Get()), v)
    92  	if err != nil {
    93  		return []error{err}
    94  	}
    95  
    96  	return nil
    97  }
    98  
    99  func SetDisabledMetric(name string) {
   100  	disabledMetricsLock.Lock()
   101  	defer disabledMetricsLock.Unlock()
   102  	disabledMetrics[name] = struct{}{}
   103  	disabledMetricsTotal.Inc()
   104  }
   105  
   106  // SetShowHidden will enable showing hidden metrics. This will no-opt
   107  // after the initial call
   108  func SetShowHidden() {
   109  	showHiddenOnce.Do(func() {
   110  		showHidden.Store(true)
   111  
   112  		// re-register collectors that has been hidden in phase of last registry.
   113  		for _, r := range registries {
   114  			r.enableHiddenCollectors()
   115  			r.enableHiddenStableCollectors()
   116  		}
   117  	})
   118  }
   119  
   120  // ShouldShowHidden returns whether showing hidden deprecated metrics
   121  // is enabled. While the primary usecase for this is internal (to determine
   122  // registration behavior) this can also be used to introspect
   123  func ShouldShowHidden() bool {
   124  	return showHidden.Load()
   125  }
   126  
   127  // Registerable is an interface for a collector metric which we
   128  // will register with KubeRegistry.
   129  type Registerable interface {
   130  	prometheus.Collector
   131  
   132  	// Create will mark deprecated state for the collector
   133  	Create(version *semver.Version) bool
   134  
   135  	// ClearState will clear all the states marked by Create.
   136  	ClearState()
   137  
   138  	// FQName returns the fully-qualified metric name of the collector.
   139  	FQName() string
   140  }
   141  
   142  type resettable interface {
   143  	Reset()
   144  }
   145  
   146  // KubeRegistry is an interface which implements a subset of prometheus.Registerer and
   147  // prometheus.Gatherer interfaces
   148  type KubeRegistry interface {
   149  	// Deprecated
   150  	RawMustRegister(...prometheus.Collector)
   151  	// CustomRegister is our internal variant of Prometheus registry.Register
   152  	CustomRegister(c StableCollector) error
   153  	// CustomMustRegister is our internal variant of Prometheus registry.MustRegister
   154  	CustomMustRegister(cs ...StableCollector)
   155  	// Register conforms to Prometheus registry.Register
   156  	Register(Registerable) error
   157  	// MustRegister conforms to Prometheus registry.MustRegister
   158  	MustRegister(...Registerable)
   159  	// Unregister conforms to Prometheus registry.Unregister
   160  	Unregister(collector Collector) bool
   161  	// Gather conforms to Prometheus gatherer.Gather
   162  	Gather() ([]*dto.MetricFamily, error)
   163  	// Reset invokes the Reset() function on all items in the registry
   164  	// which are added as resettables.
   165  	Reset()
   166  	// RegisterMetaMetrics registers metrics about the number of registered metrics.
   167  	RegisterMetaMetrics()
   168  	// Registerer exposes the underlying prometheus registerer
   169  	Registerer() prometheus.Registerer
   170  	// Gatherer exposes the underlying prometheus gatherer
   171  	Gatherer() prometheus.Gatherer
   172  }
   173  
   174  // kubeRegistry is a wrapper around a prometheus registry-type object. Upon initialization
   175  // the kubernetes binary version information is loaded into the registry object, so that
   176  // automatic behavior can be configured for metric versioning.
   177  type kubeRegistry struct {
   178  	PromRegistry
   179  	version              semver.Version
   180  	hiddenCollectors     map[string]Registerable // stores all collectors that has been hidden
   181  	stableCollectors     []StableCollector       // stores all stable collector
   182  	hiddenCollectorsLock sync.RWMutex
   183  	stableCollectorsLock sync.RWMutex
   184  	resetLock            sync.RWMutex
   185  	resettables          []resettable
   186  }
   187  
   188  // Register registers a new Collector to be included in metrics
   189  // collection. It returns an error if the descriptors provided by the
   190  // Collector are invalid or if they — in combination with descriptors of
   191  // already registered Collectors — do not fulfill the consistency and
   192  // uniqueness criteria described in the documentation of metric.Desc.
   193  func (kr *kubeRegistry) Register(c Registerable) error {
   194  	if c.Create(&kr.version) {
   195  		defer kr.addResettable(c)
   196  		return kr.PromRegistry.Register(c)
   197  	}
   198  
   199  	kr.trackHiddenCollector(c)
   200  	return nil
   201  }
   202  
   203  // Registerer exposes the underlying prometheus.Registerer
   204  func (kr *kubeRegistry) Registerer() prometheus.Registerer {
   205  	return kr.PromRegistry
   206  }
   207  
   208  // Gatherer exposes the underlying prometheus.Gatherer
   209  func (kr *kubeRegistry) Gatherer() prometheus.Gatherer {
   210  	return kr.PromRegistry
   211  }
   212  
   213  // MustRegister works like Register but registers any number of
   214  // Collectors and panics upon the first registration that causes an
   215  // error.
   216  func (kr *kubeRegistry) MustRegister(cs ...Registerable) {
   217  	metrics := make([]prometheus.Collector, 0, len(cs))
   218  	for _, c := range cs {
   219  		if c.Create(&kr.version) {
   220  			metrics = append(metrics, c)
   221  			kr.addResettable(c)
   222  		} else {
   223  			kr.trackHiddenCollector(c)
   224  		}
   225  	}
   226  	kr.PromRegistry.MustRegister(metrics...)
   227  }
   228  
   229  // CustomRegister registers a new custom collector.
   230  func (kr *kubeRegistry) CustomRegister(c StableCollector) error {
   231  	kr.trackStableCollectors(c)
   232  	defer kr.addResettable(c)
   233  	if c.Create(&kr.version, c) {
   234  		return kr.PromRegistry.Register(c)
   235  	}
   236  	return nil
   237  }
   238  
   239  // CustomMustRegister works like CustomRegister but registers any number of
   240  // StableCollectors and panics upon the first registration that causes an
   241  // error.
   242  func (kr *kubeRegistry) CustomMustRegister(cs ...StableCollector) {
   243  	kr.trackStableCollectors(cs...)
   244  	collectors := make([]prometheus.Collector, 0, len(cs))
   245  	for _, c := range cs {
   246  		if c.Create(&kr.version, c) {
   247  			kr.addResettable(c)
   248  			collectors = append(collectors, c)
   249  		}
   250  	}
   251  	kr.PromRegistry.MustRegister(collectors...)
   252  }
   253  
   254  // RawMustRegister takes a native prometheus.Collector and registers the collector
   255  // to the registry. This bypasses metrics safety checks, so should only be used
   256  // to register custom prometheus collectors.
   257  //
   258  // Deprecated
   259  func (kr *kubeRegistry) RawMustRegister(cs ...prometheus.Collector) {
   260  	kr.PromRegistry.MustRegister(cs...)
   261  	for _, c := range cs {
   262  		kr.addResettable(c)
   263  	}
   264  }
   265  
   266  // addResettable will automatically add our metric to our reset
   267  // list if it satisfies the interface
   268  func (kr *kubeRegistry) addResettable(i interface{}) {
   269  	kr.resetLock.Lock()
   270  	defer kr.resetLock.Unlock()
   271  	if resettable, ok := i.(resettable); ok {
   272  		kr.resettables = append(kr.resettables, resettable)
   273  	}
   274  }
   275  
   276  // Unregister unregisters the Collector that equals the Collector passed
   277  // in as an argument.  (Two Collectors are considered equal if their
   278  // Describe method yields the same set of descriptors.) The function
   279  // returns whether a Collector was unregistered. Note that an unchecked
   280  // Collector cannot be unregistered (as its Describe method does not
   281  // yield any descriptor).
   282  func (kr *kubeRegistry) Unregister(collector Collector) bool {
   283  	return kr.PromRegistry.Unregister(collector)
   284  }
   285  
   286  // Gather calls the Collect method of the registered Collectors and then
   287  // gathers the collected metrics into a lexicographically sorted slice
   288  // of uniquely named MetricFamily protobufs. Gather ensures that the
   289  // returned slice is valid and self-consistent so that it can be used
   290  // for valid exposition. As an exception to the strict consistency
   291  // requirements described for metric.Desc, Gather will tolerate
   292  // different sets of label names for metrics of the same metric family.
   293  func (kr *kubeRegistry) Gather() ([]*dto.MetricFamily, error) {
   294  	return kr.PromRegistry.Gather()
   295  }
   296  
   297  // trackHiddenCollector stores all hidden collectors.
   298  func (kr *kubeRegistry) trackHiddenCollector(c Registerable) {
   299  	kr.hiddenCollectorsLock.Lock()
   300  	defer kr.hiddenCollectorsLock.Unlock()
   301  
   302  	kr.hiddenCollectors[c.FQName()] = c
   303  	hiddenMetricsTotal.Inc()
   304  }
   305  
   306  // trackStableCollectors stores all custom collectors.
   307  func (kr *kubeRegistry) trackStableCollectors(cs ...StableCollector) {
   308  	kr.stableCollectorsLock.Lock()
   309  	defer kr.stableCollectorsLock.Unlock()
   310  
   311  	kr.stableCollectors = append(kr.stableCollectors, cs...)
   312  }
   313  
   314  // enableHiddenCollectors will re-register all of the hidden collectors.
   315  func (kr *kubeRegistry) enableHiddenCollectors() {
   316  	if len(kr.hiddenCollectors) == 0 {
   317  		return
   318  	}
   319  
   320  	kr.hiddenCollectorsLock.Lock()
   321  	cs := make([]Registerable, 0, len(kr.hiddenCollectors))
   322  
   323  	for _, c := range kr.hiddenCollectors {
   324  		c.ClearState()
   325  		cs = append(cs, c)
   326  	}
   327  
   328  	kr.hiddenCollectors = make(map[string]Registerable)
   329  	kr.hiddenCollectorsLock.Unlock()
   330  	kr.MustRegister(cs...)
   331  }
   332  
   333  // enableHiddenStableCollectors will re-register the stable collectors if there is one or more hidden metrics in it.
   334  // Since we can not register a metrics twice, so we have to unregister first then register again.
   335  func (kr *kubeRegistry) enableHiddenStableCollectors() {
   336  	if len(kr.stableCollectors) == 0 {
   337  		return
   338  	}
   339  
   340  	kr.stableCollectorsLock.Lock()
   341  
   342  	cs := make([]StableCollector, 0, len(kr.stableCollectors))
   343  	for _, c := range kr.stableCollectors {
   344  		if len(c.HiddenMetrics()) > 0 {
   345  			kr.Unregister(c) // unregister must happens before clear state, otherwise no metrics would be unregister
   346  			c.ClearState()
   347  			cs = append(cs, c)
   348  		}
   349  	}
   350  
   351  	kr.stableCollectors = nil
   352  	kr.stableCollectorsLock.Unlock()
   353  	kr.CustomMustRegister(cs...)
   354  }
   355  
   356  // Reset invokes Reset on all metrics that are resettable.
   357  func (kr *kubeRegistry) Reset() {
   358  	kr.resetLock.RLock()
   359  	defer kr.resetLock.RUnlock()
   360  	for _, r := range kr.resettables {
   361  		r.Reset()
   362  	}
   363  }
   364  
   365  // BuildVersion is a helper function that can be easily mocked.
   366  var BuildVersion = version.Get
   367  
   368  func newKubeRegistry(v apimachineryversion.Info) *kubeRegistry {
   369  	r := &kubeRegistry{
   370  		PromRegistry:     prometheus.NewRegistry(),
   371  		version:          parseVersion(v),
   372  		hiddenCollectors: make(map[string]Registerable),
   373  		resettables:      make([]resettable, 0),
   374  	}
   375  
   376  	registriesLock.Lock()
   377  	defer registriesLock.Unlock()
   378  	registries = append(registries, r)
   379  
   380  	return r
   381  }
   382  
   383  // NewKubeRegistry creates a new vanilla Registry
   384  func NewKubeRegistry() KubeRegistry {
   385  	r := newKubeRegistry(BuildVersion())
   386  	return r
   387  }
   388  
   389  func (r *kubeRegistry) RegisterMetaMetrics() {
   390  	r.MustRegister(registeredMetricsTotal)
   391  	r.MustRegister(disabledMetricsTotal)
   392  	r.MustRegister(hiddenMetricsTotal)
   393  	r.MustRegister(cardinalityEnforcementUnexpectedCategorizationsTotal)
   394  }
   395  

View as plain text