2020-01-14 10:38:55 +00:00
|
|
|
/*
|
|
|
|
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 (
|
|
|
|
"fmt"
|
|
|
|
"sync"
|
|
|
|
|
2022-05-05 02:47:06 +00:00
|
|
|
"github.com/blang/semver/v4"
|
2020-01-14 10:38:55 +00:00
|
|
|
"github.com/prometheus/client_golang/prometheus"
|
|
|
|
|
2020-12-17 12:28:29 +00:00
|
|
|
"k8s.io/klog/v2"
|
2020-01-14 10:38:55 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// Desc is a prometheus.Desc extension.
|
|
|
|
//
|
|
|
|
// Use NewDesc to create new Desc instances.
|
|
|
|
type Desc struct {
|
|
|
|
// fqName has been built from Namespace, Subsystem, and Name.
|
|
|
|
fqName string
|
|
|
|
// help provides some helpful information about this metric.
|
|
|
|
help string
|
|
|
|
// constLabels is the label names. Their label values are variable.
|
|
|
|
constLabels Labels
|
|
|
|
// variableLabels contains names of labels for which the metric
|
|
|
|
// maintains variable values.
|
|
|
|
variableLabels []string
|
|
|
|
|
|
|
|
// promDesc is the descriptor used by every Prometheus Metric.
|
2020-04-14 07:04:33 +00:00
|
|
|
promDesc *prometheus.Desc
|
|
|
|
annotatedHelp string
|
2020-01-14 10:38:55 +00:00
|
|
|
|
|
|
|
// stabilityLevel represents the API guarantees for a given defined metric.
|
|
|
|
stabilityLevel StabilityLevel
|
|
|
|
// deprecatedVersion represents in which version this metric be deprecated.
|
|
|
|
deprecatedVersion string
|
|
|
|
|
|
|
|
isDeprecated bool
|
|
|
|
isHidden bool
|
|
|
|
isCreated bool
|
|
|
|
createLock sync.RWMutex
|
|
|
|
markDeprecationOnce sync.Once
|
|
|
|
createOnce sync.Once
|
|
|
|
deprecateOnce sync.Once
|
|
|
|
hideOnce sync.Once
|
|
|
|
annotateOnce sync.Once
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewDesc extends prometheus.NewDesc with stability support.
|
|
|
|
//
|
|
|
|
// The stabilityLevel should be valid stability label, such as "metrics.ALPHA"
|
|
|
|
// and "metrics.STABLE"(Maybe "metrics.BETA" in future). Default value "metrics.ALPHA"
|
|
|
|
// will be used in case of empty or invalid stability label.
|
|
|
|
//
|
|
|
|
// The deprecatedVersion represents in which version this Metric be deprecated.
|
|
|
|
// The deprecation policy outlined by the control plane metrics stability KEP.
|
|
|
|
func NewDesc(fqName string, help string, variableLabels []string, constLabels Labels,
|
|
|
|
stabilityLevel StabilityLevel, deprecatedVersion string) *Desc {
|
|
|
|
d := &Desc{
|
|
|
|
fqName: fqName,
|
|
|
|
help: help,
|
2020-04-14 07:04:33 +00:00
|
|
|
annotatedHelp: help,
|
2020-01-14 10:38:55 +00:00
|
|
|
variableLabels: variableLabels,
|
|
|
|
constLabels: constLabels,
|
|
|
|
stabilityLevel: stabilityLevel,
|
|
|
|
deprecatedVersion: deprecatedVersion,
|
|
|
|
}
|
|
|
|
d.stabilityLevel.setDefaults()
|
|
|
|
|
|
|
|
return d
|
|
|
|
}
|
|
|
|
|
|
|
|
// String formats the Desc as a string.
|
|
|
|
// The stability metadata maybe annotated in 'HELP' section if called after registry,
|
|
|
|
// otherwise not.
|
2020-04-14 07:04:33 +00:00
|
|
|
// e.g. "Desc{fqName: "normal_stable_descriptor", help: "[STABLE] this is a stable descriptor", constLabels: {}, variableLabels: []}"
|
2020-01-14 10:38:55 +00:00
|
|
|
func (d *Desc) String() string {
|
|
|
|
if d.isCreated {
|
|
|
|
return d.promDesc.String()
|
|
|
|
}
|
|
|
|
|
|
|
|
return prometheus.NewDesc(d.fqName, d.help, d.variableLabels, prometheus.Labels(d.constLabels)).String()
|
|
|
|
}
|
|
|
|
|
|
|
|
// toPrometheusDesc transform self to prometheus.Desc
|
|
|
|
func (d *Desc) toPrometheusDesc() *prometheus.Desc {
|
|
|
|
return d.promDesc
|
|
|
|
}
|
|
|
|
|
|
|
|
// DeprecatedVersion returns a pointer to the Version or nil
|
|
|
|
func (d *Desc) DeprecatedVersion() *semver.Version {
|
|
|
|
return parseSemver(d.deprecatedVersion)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
func (d *Desc) determineDeprecationStatus(version semver.Version) {
|
|
|
|
selfVersion := d.DeprecatedVersion()
|
|
|
|
if selfVersion == nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
d.markDeprecationOnce.Do(func() {
|
|
|
|
if selfVersion.LTE(version) {
|
|
|
|
d.isDeprecated = true
|
|
|
|
}
|
|
|
|
if ShouldShowHidden() {
|
|
|
|
klog.Warningf("Hidden metrics(%s) have been manually overridden, showing this very deprecated metric.", d.fqName)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if shouldHide(&version, selfVersion) {
|
|
|
|
// TODO(RainbowMango): Remove this log temporarily. https://github.com/kubernetes/kubernetes/issues/85369
|
|
|
|
// klog.Warningf("This metric(%s) has been deprecated for more than one release, hiding.", d.fqName)
|
|
|
|
d.isHidden = true
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
// IsHidden returns if metric will be hidden
|
|
|
|
func (d *Desc) IsHidden() bool {
|
|
|
|
return d.isHidden
|
|
|
|
}
|
|
|
|
|
|
|
|
// IsDeprecated returns if metric has been deprecated
|
|
|
|
func (d *Desc) IsDeprecated() bool {
|
|
|
|
return d.isDeprecated
|
|
|
|
}
|
|
|
|
|
2020-04-14 07:04:33 +00:00
|
|
|
// IsCreated returns if metric has been created.
|
|
|
|
func (d *Desc) IsCreated() bool {
|
|
|
|
d.createLock.RLock()
|
|
|
|
defer d.createLock.RUnlock()
|
|
|
|
|
|
|
|
return d.isCreated
|
|
|
|
}
|
|
|
|
|
|
|
|
// create forces the initialization of Desc which has been deferred until
|
|
|
|
// the point at which this method is invoked. This method will determine whether
|
|
|
|
// the Desc is deprecated or hidden, no-opting if the Desc should be considered
|
|
|
|
// hidden. Furthermore, this function no-opts and returns true if Desc is already
|
|
|
|
// created.
|
|
|
|
func (d *Desc) create(version *semver.Version) bool {
|
|
|
|
if version != nil {
|
|
|
|
d.determineDeprecationStatus(*version)
|
|
|
|
}
|
|
|
|
|
|
|
|
// let's not create if this metric is slated to be hidden
|
|
|
|
if d.IsHidden() {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
d.createOnce.Do(func() {
|
|
|
|
d.createLock.Lock()
|
|
|
|
defer d.createLock.Unlock()
|
|
|
|
|
|
|
|
d.isCreated = true
|
|
|
|
if d.IsDeprecated() {
|
|
|
|
d.initializeDeprecatedDesc()
|
|
|
|
} else {
|
|
|
|
d.initialize()
|
|
|
|
}
|
|
|
|
})
|
|
|
|
return d.IsCreated()
|
|
|
|
}
|
|
|
|
|
|
|
|
// ClearState will clear all the states marked by Create.
|
|
|
|
// It intends to be used for re-register a hidden metric.
|
|
|
|
func (d *Desc) ClearState() {
|
|
|
|
d.isDeprecated = false
|
|
|
|
d.isHidden = false
|
|
|
|
d.isCreated = false
|
|
|
|
|
|
|
|
d.markDeprecationOnce = *new(sync.Once)
|
|
|
|
d.createOnce = *new(sync.Once)
|
|
|
|
d.deprecateOnce = *new(sync.Once)
|
|
|
|
d.hideOnce = *new(sync.Once)
|
|
|
|
d.annotateOnce = *new(sync.Once)
|
|
|
|
|
|
|
|
d.annotatedHelp = d.help
|
|
|
|
d.promDesc = nil
|
|
|
|
}
|
|
|
|
|
2020-01-14 10:38:55 +00:00
|
|
|
func (d *Desc) markDeprecated() {
|
|
|
|
d.deprecateOnce.Do(func() {
|
2020-04-14 07:04:33 +00:00
|
|
|
d.annotatedHelp = fmt.Sprintf("(Deprecated since %s) %s", d.deprecatedVersion, d.annotatedHelp)
|
2020-01-14 10:38:55 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func (d *Desc) annotateStabilityLevel() {
|
|
|
|
d.annotateOnce.Do(func() {
|
2020-04-14 07:04:33 +00:00
|
|
|
d.annotatedHelp = fmt.Sprintf("[%v] %v", d.stabilityLevel, d.annotatedHelp)
|
2020-01-14 10:38:55 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func (d *Desc) initialize() {
|
|
|
|
d.annotateStabilityLevel()
|
2020-04-14 07:04:33 +00:00
|
|
|
|
2020-01-14 10:38:55 +00:00
|
|
|
// this actually creates the underlying prometheus desc.
|
2020-04-14 07:04:33 +00:00
|
|
|
d.promDesc = prometheus.NewDesc(d.fqName, d.annotatedHelp, d.variableLabels, prometheus.Labels(d.constLabels))
|
2020-01-14 10:38:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (d *Desc) initializeDeprecatedDesc() {
|
|
|
|
d.markDeprecated()
|
|
|
|
d.initialize()
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetRawDesc will returns a new *Desc with original parameters provided to NewDesc().
|
|
|
|
//
|
|
|
|
// It will be useful in testing scenario that the same Desc be registered to different registry.
|
2022-08-24 02:24:25 +00:00
|
|
|
// 1. Desc `D` is registered to registry 'A' in TestA (Note: `D` maybe created)
|
|
|
|
// 2. Desc `D` is registered to registry 'B' in TestB (Note: since 'D' has been created once, thus will be ignored by registry 'B')
|
2020-01-14 10:38:55 +00:00
|
|
|
func (d *Desc) GetRawDesc() *Desc {
|
2020-04-14 07:04:33 +00:00
|
|
|
return NewDesc(d.fqName, d.help, d.variableLabels, d.constLabels, d.stabilityLevel, d.deprecatedVersion)
|
2020-01-14 10:38:55 +00:00
|
|
|
}
|