2020-01-14 16:08:55 +05:30
/ *
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 (
2021-06-25 10:29:51 +05:30
"context"
2025-01-16 09:41:46 +05:30
"sync"
2022-08-24 07:54:25 +05:30
2022-05-05 08:17:06 +05:30
"github.com/blang/semver/v4"
2020-01-14 16:08:55 +05:30
"github.com/prometheus/client_golang/prometheus"
2025-01-16 09:41:46 +05:30
"go.opentelemetry.io/otel/trace"
2020-01-14 16:08:55 +05:30
)
// Histogram is our internal representation for our wrapping struct around prometheus
// histograms. Summary implements both kubeCollector and ObserverMetric
type Histogram struct {
2025-01-16 09:41:46 +05:30
ctx context . Context
2020-01-14 16:08:55 +05:30
ObserverMetric
* HistogramOpts
lazyMetric
selfCollector
}
2025-01-16 09:41:46 +05:30
// exemplarHistogramMetric holds a context to extract exemplar labels from, and a historgram metric to attach them to. It implements the metricWithExemplar interface.
type exemplarHistogramMetric struct {
* Histogram
}
type exemplarHistogramVec struct {
* HistogramVecWithContext
observer prometheus . Observer
}
func ( h * Histogram ) Observe ( v float64 ) {
h . withExemplar ( v )
}
// withExemplar initializes the exemplarMetric object and sets the exemplar value.
func ( h * Histogram ) withExemplar ( v float64 ) {
( & exemplarHistogramMetric { h } ) . withExemplar ( v )
}
// withExemplar attaches an exemplar to the metric.
func ( e * exemplarHistogramMetric ) withExemplar ( v float64 ) {
if m , ok := e . Histogram . ObserverMetric . ( prometheus . ExemplarObserver ) ; ok {
maybeSpanCtx := trace . SpanContextFromContext ( e . ctx )
if maybeSpanCtx . IsValid ( ) && maybeSpanCtx . IsSampled ( ) {
exemplarLabels := prometheus . Labels {
"trace_id" : maybeSpanCtx . TraceID ( ) . String ( ) ,
"span_id" : maybeSpanCtx . SpanID ( ) . String ( ) ,
}
m . ObserveWithExemplar ( v , exemplarLabels )
return
}
}
e . ObserverMetric . Observe ( v )
}
2020-01-14 16:08:55 +05:30
// NewHistogram returns an object which is Histogram-like. However, nothing
// will be measured until the histogram is registered somewhere.
func NewHistogram ( opts * HistogramOpts ) * Histogram {
opts . StabilityLevel . setDefaults ( )
h := & Histogram {
HistogramOpts : opts ,
2023-02-01 18:06:36 +01:00
lazyMetric : lazyMetric { stabilityLevel : opts . StabilityLevel } ,
2020-01-14 16:08:55 +05:30
}
h . setPrometheusHistogram ( noopMetric { } )
2020-04-14 12:34:33 +05:30
h . lazyInit ( h , BuildFQName ( opts . Namespace , opts . Subsystem , opts . Name ) )
2020-01-14 16:08:55 +05:30
return h
}
// setPrometheusHistogram sets the underlying KubeGauge object, i.e. the thing that does the measurement.
func ( h * Histogram ) setPrometheusHistogram ( histogram prometheus . Histogram ) {
h . ObserverMetric = histogram
h . initSelfCollection ( histogram )
}
// DeprecatedVersion returns a pointer to the Version or nil
func ( h * Histogram ) DeprecatedVersion ( ) * semver . Version {
return parseSemver ( h . HistogramOpts . DeprecatedVersion )
}
// initializeMetric invokes the actual prometheus.Histogram object instantiation
// and stores a reference to it
func ( h * Histogram ) initializeMetric ( ) {
h . HistogramOpts . annotateStabilityLevel ( )
// this actually creates the underlying prometheus gauge.
h . setPrometheusHistogram ( prometheus . NewHistogram ( h . HistogramOpts . toPromHistogramOpts ( ) ) )
}
// initializeDeprecatedMetric invokes the actual prometheus.Histogram object instantiation
// but modifies the Help description prior to object instantiation.
func ( h * Histogram ) initializeDeprecatedMetric ( ) {
h . HistogramOpts . markDeprecated ( )
h . initializeMetric ( )
}
2021-06-25 10:29:51 +05:30
// WithContext allows the normal Histogram metric to pass in context. The context is no-op now.
func ( h * Histogram ) WithContext ( ctx context . Context ) ObserverMetric {
2025-01-16 09:41:46 +05:30
h . ctx = ctx
2021-06-25 10:29:51 +05:30
return h . ObserverMetric
}
2020-01-14 16:08:55 +05:30
// HistogramVec is the internal representation of our wrapping struct around prometheus
// histogramVecs.
type HistogramVec struct {
* prometheus . HistogramVec
* HistogramOpts
lazyMetric
originalLabels [ ] string
}
// NewHistogramVec returns an object which satisfies kubeCollector and wraps the
// prometheus.HistogramVec object. However, the object returned will not measure
2022-08-24 07:54:25 +05:30
// anything unless the collector is first registered, since the metric is lazily instantiated,
// and only members extracted after
// registration will actually measure anything.
2020-01-14 16:08:55 +05:30
func NewHistogramVec ( opts * HistogramOpts , labels [ ] string ) * HistogramVec {
opts . StabilityLevel . setDefaults ( )
2021-06-25 10:29:51 +05:30
fqName := BuildFQName ( opts . Namespace , opts . Subsystem , opts . Name )
2020-01-14 16:08:55 +05:30
v := & HistogramVec {
HistogramVec : noopHistogramVec ,
HistogramOpts : opts ,
originalLabels : labels ,
2023-02-01 18:06:36 +01:00
lazyMetric : lazyMetric { stabilityLevel : opts . StabilityLevel } ,
2020-01-14 16:08:55 +05:30
}
2021-06-25 10:29:51 +05:30
v . lazyInit ( v , fqName )
2020-01-14 16:08:55 +05:30
return v
}
// DeprecatedVersion returns a pointer to the Version or nil
func ( v * HistogramVec ) DeprecatedVersion ( ) * semver . Version {
return parseSemver ( v . HistogramOpts . DeprecatedVersion )
}
func ( v * HistogramVec ) initializeMetric ( ) {
v . HistogramOpts . annotateStabilityLevel ( )
v . HistogramVec = prometheus . NewHistogramVec ( v . HistogramOpts . toPromHistogramOpts ( ) , v . originalLabels )
}
func ( v * HistogramVec ) initializeDeprecatedMetric ( ) {
v . HistogramOpts . markDeprecated ( )
v . initializeMetric ( )
}
2022-08-24 07:54:25 +05:30
// Default Prometheus Vec behavior is that member extraction results in creation of a new element
// if one with the unique label values is not found in the underlying stored metricMap.
2020-01-14 16:08:55 +05:30
// This means that if this function is called but the underlying metric is not registered
// (which means it will never be exposed externally nor consumed), the metric will exist in memory
// for perpetuity (i.e. throughout application lifecycle).
//
// For reference: https://github.com/prometheus/client_golang/blob/v0.9.2/prometheus/histogram.go#L460-L470
2022-08-24 07:54:25 +05:30
//
// In contrast, the Vec behavior in this package is that member extraction before registration
// returns a permanent noop object.
2020-01-14 16:08:55 +05:30
// WithLabelValues returns the ObserverMetric for the given slice of label
// values (same order as the VariableLabels in Desc). If that combination of
// label values is accessed for the first time, a new ObserverMetric is created IFF the HistogramVec
// has been registered to a metrics registry.
func ( v * HistogramVec ) WithLabelValues ( lvs ... string ) ObserverMetric {
if ! v . IsCreated ( ) {
return noop
}
2021-06-25 10:29:51 +05:30
if v . LabelValueAllowLists != nil {
v . LabelValueAllowLists . ConstrainToAllowedList ( v . originalLabels , lvs )
2025-01-16 09:41:46 +05:30
} else {
v . initializeLabelAllowListsOnce . Do ( func ( ) {
allowListLock . RLock ( )
if allowList , ok := labelValueAllowLists [ v . FQName ( ) ] ; ok {
v . LabelValueAllowLists = allowList
allowList . ConstrainToAllowedList ( v . originalLabels , lvs )
}
allowListLock . RUnlock ( )
} )
2021-06-25 10:29:51 +05:30
}
2020-01-14 16:08:55 +05:30
return v . HistogramVec . WithLabelValues ( lvs ... )
}
// With returns the ObserverMetric for the given Labels map (the label names
// must match those of the VariableLabels in Desc). If that label map is
// accessed for the first time, a new ObserverMetric is created IFF the HistogramVec has
// been registered to a metrics registry.
func ( v * HistogramVec ) With ( labels map [ string ] string ) ObserverMetric {
if ! v . IsCreated ( ) {
return noop
}
2021-06-25 10:29:51 +05:30
if v . LabelValueAllowLists != nil {
v . LabelValueAllowLists . ConstrainLabelMap ( labels )
2025-01-16 09:41:46 +05:30
} else {
v . initializeLabelAllowListsOnce . Do ( func ( ) {
allowListLock . RLock ( )
if allowList , ok := labelValueAllowLists [ v . FQName ( ) ] ; ok {
v . LabelValueAllowLists = allowList
allowList . ConstrainLabelMap ( labels )
}
allowListLock . RUnlock ( )
} )
2021-06-25 10:29:51 +05:30
}
2020-01-14 16:08:55 +05:30
return v . HistogramVec . With ( labels )
}
// Delete deletes the metric where the variable labels are the same as those
// passed in as labels. It returns true if a metric was deleted.
//
// It is not an error if the number and names of the Labels are inconsistent
// with those of the VariableLabels in Desc. However, such inconsistent Labels
// can never match an actual metric, so the method will always return false in
// that case.
func ( v * HistogramVec ) Delete ( labels map [ string ] string ) bool {
if ! v . IsCreated ( ) {
return false // since we haven't created the metric, we haven't deleted a metric with the passed in values
}
return v . HistogramVec . Delete ( labels )
}
// Reset deletes all metrics in this vector.
func ( v * HistogramVec ) Reset ( ) {
if ! v . IsCreated ( ) {
return
}
v . HistogramVec . Reset ( )
}
2021-06-25 10:29:51 +05:30
2025-01-16 09:41:46 +05:30
// ResetLabelAllowLists resets the label allow list for the HistogramVec.
// NOTE: This should only be used in test.
func ( v * HistogramVec ) ResetLabelAllowLists ( ) {
v . initializeLabelAllowListsOnce = sync . Once { }
v . LabelValueAllowLists = nil
}
2021-06-25 10:29:51 +05:30
// WithContext returns wrapped HistogramVec with context
func ( v * HistogramVec ) WithContext ( ctx context . Context ) * HistogramVecWithContext {
return & HistogramVecWithContext {
ctx : ctx ,
2022-05-05 08:17:06 +05:30
HistogramVec : v ,
2021-06-25 10:29:51 +05:30
}
}
// HistogramVecWithContext is the wrapper of HistogramVec with context.
type HistogramVecWithContext struct {
2022-05-05 08:17:06 +05:30
* HistogramVec
2021-06-25 10:29:51 +05:30
ctx context . Context
}
2025-01-16 09:41:46 +05:30
func ( h * exemplarHistogramVec ) Observe ( v float64 ) {
h . withExemplar ( v )
}
func ( h * exemplarHistogramVec ) withExemplar ( v float64 ) {
if m , ok := h . observer . ( prometheus . ExemplarObserver ) ; ok {
maybeSpanCtx := trace . SpanContextFromContext ( h . HistogramVecWithContext . ctx )
if maybeSpanCtx . IsValid ( ) && maybeSpanCtx . IsSampled ( ) {
m . ObserveWithExemplar ( v , prometheus . Labels {
"trace_id" : maybeSpanCtx . TraceID ( ) . String ( ) ,
"span_id" : maybeSpanCtx . SpanID ( ) . String ( ) ,
} )
return
}
}
h . observer . Observe ( v )
}
2021-06-25 10:29:51 +05:30
// WithLabelValues is the wrapper of HistogramVec.WithLabelValues.
2025-01-16 09:41:46 +05:30
func ( vc * HistogramVecWithContext ) WithLabelValues ( lvs ... string ) * exemplarHistogramVec {
return & exemplarHistogramVec {
HistogramVecWithContext : vc ,
observer : vc . HistogramVec . WithLabelValues ( lvs ... ) ,
}
2021-06-25 10:29:51 +05:30
}
// With is the wrapper of HistogramVec.With.
2025-01-16 09:41:46 +05:30
func ( vc * HistogramVecWithContext ) With ( labels map [ string ] string ) * exemplarHistogramVec {
return & exemplarHistogramVec {
HistogramVecWithContext : vc ,
observer : vc . HistogramVec . With ( labels ) ,
}
2021-06-25 10:29:51 +05:30
}