...

Source file src/k8s.io/component-base/metrics/desc.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  
    23  	"github.com/blang/semver/v4"
    24  	"github.com/prometheus/client_golang/prometheus"
    25  
    26  	"k8s.io/klog/v2"
    27  )
    28  
    29  // Desc is a prometheus.Desc extension.
    30  //
    31  // Use NewDesc to create new Desc instances.
    32  type Desc struct {
    33  	// fqName has been built from Namespace, Subsystem, and Name.
    34  	fqName string
    35  	// help provides some helpful information about this metric.
    36  	help string
    37  	// constLabels is the label names. Their label values are variable.
    38  	constLabels Labels
    39  	// variableLabels contains names of labels for which the metric
    40  	// maintains variable values.
    41  	variableLabels []string
    42  
    43  	// promDesc is the descriptor used by every Prometheus Metric.
    44  	promDesc      *prometheus.Desc
    45  	annotatedHelp string
    46  
    47  	// stabilityLevel represents the API guarantees for a given defined metric.
    48  	stabilityLevel StabilityLevel
    49  	// deprecatedVersion represents in which version this metric be deprecated.
    50  	deprecatedVersion string
    51  
    52  	isDeprecated        bool
    53  	isHidden            bool
    54  	isCreated           bool
    55  	createLock          sync.RWMutex
    56  	markDeprecationOnce sync.Once
    57  	createOnce          sync.Once
    58  	deprecateOnce       sync.Once
    59  	hideOnce            sync.Once
    60  	annotateOnce        sync.Once
    61  }
    62  
    63  // NewDesc extends prometheus.NewDesc with stability support.
    64  //
    65  // The stabilityLevel should be valid stability label, such as "metrics.ALPHA"
    66  // and "metrics.STABLE"(Maybe "metrics.BETA" in future). Default value "metrics.ALPHA"
    67  // will be used in case of empty or invalid stability label.
    68  //
    69  // The deprecatedVersion represents in which version this Metric be deprecated.
    70  // The deprecation policy outlined by the control plane metrics stability KEP.
    71  func NewDesc(fqName string, help string, variableLabels []string, constLabels Labels,
    72  	stabilityLevel StabilityLevel, deprecatedVersion string) *Desc {
    73  	d := &Desc{
    74  		fqName:            fqName,
    75  		help:              help,
    76  		annotatedHelp:     help,
    77  		variableLabels:    variableLabels,
    78  		constLabels:       constLabels,
    79  		stabilityLevel:    stabilityLevel,
    80  		deprecatedVersion: deprecatedVersion,
    81  	}
    82  	d.stabilityLevel.setDefaults()
    83  
    84  	return d
    85  }
    86  
    87  // String formats the Desc as a string.
    88  // The stability metadata maybe annotated in 'HELP' section if called after registry,
    89  // otherwise not.
    90  // e.g. "Desc{fqName: "normal_stable_descriptor", help: "[STABLE] this is a stable descriptor", constLabels: {}, variableLabels: []}"
    91  func (d *Desc) String() string {
    92  	if d.isCreated {
    93  		return d.promDesc.String()
    94  	}
    95  
    96  	return prometheus.NewDesc(d.fqName, d.help, d.variableLabels, prometheus.Labels(d.constLabels)).String()
    97  }
    98  
    99  // toPrometheusDesc transform self to prometheus.Desc
   100  func (d *Desc) toPrometheusDesc() *prometheus.Desc {
   101  	return d.promDesc
   102  }
   103  
   104  // DeprecatedVersion returns a pointer to the Version or nil
   105  func (d *Desc) DeprecatedVersion() *semver.Version {
   106  	return parseSemver(d.deprecatedVersion)
   107  
   108  }
   109  
   110  func (d *Desc) determineDeprecationStatus(version semver.Version) {
   111  	selfVersion := d.DeprecatedVersion()
   112  	if selfVersion == nil {
   113  		return
   114  	}
   115  	d.markDeprecationOnce.Do(func() {
   116  		if selfVersion.LTE(version) {
   117  			d.isDeprecated = true
   118  		}
   119  		if ShouldShowHidden() {
   120  			klog.Warningf("Hidden metrics(%s) have been manually overridden, showing this very deprecated metric.", d.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(%s) has been deprecated for more than one release, hiding.", d.fqName)
   126  			d.isHidden = true
   127  		}
   128  	})
   129  }
   130  
   131  // IsHidden returns if metric will be hidden
   132  func (d *Desc) IsHidden() bool {
   133  	return d.isHidden
   134  }
   135  
   136  // IsDeprecated returns if metric has been deprecated
   137  func (d *Desc) IsDeprecated() bool {
   138  	return d.isDeprecated
   139  }
   140  
   141  // IsCreated returns if metric has been created.
   142  func (d *Desc) IsCreated() bool {
   143  	d.createLock.RLock()
   144  	defer d.createLock.RUnlock()
   145  
   146  	return d.isCreated
   147  }
   148  
   149  // create forces the initialization of Desc which has been deferred until
   150  // the point at which this method is invoked. This method will determine whether
   151  // the Desc is deprecated or hidden, no-opting if the Desc should be considered
   152  // hidden. Furthermore, this function no-opts and returns true if Desc is already
   153  // created.
   154  func (d *Desc) create(version *semver.Version) bool {
   155  	if version != nil {
   156  		d.determineDeprecationStatus(*version)
   157  	}
   158  
   159  	// let's not create if this metric is slated to be hidden
   160  	if d.IsHidden() {
   161  		return false
   162  	}
   163  	d.createOnce.Do(func() {
   164  		d.createLock.Lock()
   165  		defer d.createLock.Unlock()
   166  
   167  		d.isCreated = true
   168  		if d.IsDeprecated() {
   169  			d.initializeDeprecatedDesc()
   170  		} else {
   171  			d.initialize()
   172  		}
   173  	})
   174  	return d.IsCreated()
   175  }
   176  
   177  // ClearState will clear all the states marked by Create.
   178  // It intends to be used for re-register a hidden metric.
   179  func (d *Desc) ClearState() {
   180  	d.isDeprecated = false
   181  	d.isHidden = false
   182  	d.isCreated = false
   183  
   184  	d.markDeprecationOnce = *new(sync.Once)
   185  	d.createOnce = *new(sync.Once)
   186  	d.deprecateOnce = *new(sync.Once)
   187  	d.hideOnce = *new(sync.Once)
   188  	d.annotateOnce = *new(sync.Once)
   189  
   190  	d.annotatedHelp = d.help
   191  	d.promDesc = nil
   192  }
   193  
   194  func (d *Desc) markDeprecated() {
   195  	d.deprecateOnce.Do(func() {
   196  		d.annotatedHelp = fmt.Sprintf("(Deprecated since %s) %s", d.deprecatedVersion, d.annotatedHelp)
   197  	})
   198  }
   199  
   200  func (d *Desc) annotateStabilityLevel() {
   201  	d.annotateOnce.Do(func() {
   202  		d.annotatedHelp = fmt.Sprintf("[%v] %v", d.stabilityLevel, d.annotatedHelp)
   203  	})
   204  }
   205  
   206  func (d *Desc) initialize() {
   207  	d.annotateStabilityLevel()
   208  
   209  	// this actually creates the underlying prometheus desc.
   210  	d.promDesc = prometheus.NewDesc(d.fqName, d.annotatedHelp, d.variableLabels, prometheus.Labels(d.constLabels))
   211  }
   212  
   213  func (d *Desc) initializeDeprecatedDesc() {
   214  	d.markDeprecated()
   215  	d.initialize()
   216  }
   217  
   218  // GetRawDesc will returns a new *Desc with original parameters provided to NewDesc().
   219  //
   220  // It will be useful in testing scenario that the same Desc be registered to different registry.
   221  //  1. Desc `D` is registered to registry 'A' in TestA (Note: `D` maybe created)
   222  //  2. Desc `D` is registered to registry 'B' in TestB (Note: since 'D' has been created once, thus will be ignored by registry 'B')
   223  func (d *Desc) GetRawDesc() *Desc {
   224  	return NewDesc(d.fqName, d.help, d.variableLabels, d.constLabels, d.stabilityLevel, d.deprecatedVersion)
   225  }
   226  

View as plain text