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 "context" 21 22 "github.com/blang/semver/v4" 23 "github.com/prometheus/client_golang/prometheus" 24 ) 25 26 const ( 27 DefAgeBuckets = prometheus.DefAgeBuckets 28 DefBufCap = prometheus.DefBufCap 29 DefMaxAge = prometheus.DefMaxAge 30 ) 31 32 // Summary is our internal representation for our wrapping struct around prometheus 33 // summaries. Summary implements both kubeCollector and ObserverMetric 34 // 35 // DEPRECATED: as per the metrics overhaul KEP 36 type Summary struct { 37 ObserverMetric 38 *SummaryOpts 39 lazyMetric 40 selfCollector 41 } 42 43 // NewSummary returns an object which is Summary-like. However, nothing 44 // will be measured until the summary is registered somewhere. 45 // 46 // DEPRECATED: as per the metrics overhaul KEP 47 func NewSummary(opts *SummaryOpts) *Summary { 48 opts.StabilityLevel.setDefaults() 49 50 s := &Summary{ 51 SummaryOpts: opts, 52 lazyMetric: lazyMetric{stabilityLevel: opts.StabilityLevel}, 53 } 54 s.setPrometheusSummary(noopMetric{}) 55 s.lazyInit(s, BuildFQName(opts.Namespace, opts.Subsystem, opts.Name)) 56 return s 57 } 58 59 // setPrometheusSummary sets the underlying KubeGauge object, i.e. the thing that does the measurement. 60 func (s *Summary) setPrometheusSummary(summary prometheus.Summary) { 61 s.ObserverMetric = summary 62 s.initSelfCollection(summary) 63 } 64 65 // DeprecatedVersion returns a pointer to the Version or nil 66 func (s *Summary) DeprecatedVersion() *semver.Version { 67 return parseSemver(s.SummaryOpts.DeprecatedVersion) 68 } 69 70 // initializeMetric invokes the actual prometheus.Summary object instantiation 71 // and stores a reference to it 72 func (s *Summary) initializeMetric() { 73 s.SummaryOpts.annotateStabilityLevel() 74 // this actually creates the underlying prometheus gauge. 75 s.setPrometheusSummary(prometheus.NewSummary(s.SummaryOpts.toPromSummaryOpts())) 76 } 77 78 // initializeDeprecatedMetric invokes the actual prometheus.Summary object instantiation 79 // but modifies the Help description prior to object instantiation. 80 func (s *Summary) initializeDeprecatedMetric() { 81 s.SummaryOpts.markDeprecated() 82 s.initializeMetric() 83 } 84 85 // WithContext allows the normal Summary metric to pass in context. The context is no-op now. 86 func (s *Summary) WithContext(ctx context.Context) ObserverMetric { 87 return s.ObserverMetric 88 } 89 90 // SummaryVec is the internal representation of our wrapping struct around prometheus 91 // summaryVecs. 92 // 93 // DEPRECATED: as per the metrics overhaul KEP 94 type SummaryVec struct { 95 *prometheus.SummaryVec 96 *SummaryOpts 97 lazyMetric 98 originalLabels []string 99 } 100 101 // NewSummaryVec returns an object which satisfies kubeCollector and wraps the 102 // prometheus.SummaryVec object. However, the object returned will not measure 103 // anything unless the collector is first registered, since the metric is lazily instantiated, 104 // and only members extracted after 105 // registration will actually measure anything. 106 // 107 // DEPRECATED: as per the metrics overhaul KEP 108 func NewSummaryVec(opts *SummaryOpts, labels []string) *SummaryVec { 109 opts.StabilityLevel.setDefaults() 110 111 fqName := BuildFQName(opts.Namespace, opts.Subsystem, opts.Name) 112 allowListLock.RLock() 113 if allowList, ok := labelValueAllowLists[fqName]; ok { 114 opts.LabelValueAllowLists = allowList 115 } 116 allowListLock.RUnlock() 117 118 v := &SummaryVec{ 119 SummaryOpts: opts, 120 originalLabels: labels, 121 lazyMetric: lazyMetric{stabilityLevel: opts.StabilityLevel}, 122 } 123 v.lazyInit(v, fqName) 124 return v 125 } 126 127 // DeprecatedVersion returns a pointer to the Version or nil 128 func (v *SummaryVec) DeprecatedVersion() *semver.Version { 129 return parseSemver(v.SummaryOpts.DeprecatedVersion) 130 } 131 132 func (v *SummaryVec) initializeMetric() { 133 v.SummaryOpts.annotateStabilityLevel() 134 v.SummaryVec = prometheus.NewSummaryVec(v.SummaryOpts.toPromSummaryOpts(), v.originalLabels) 135 } 136 137 func (v *SummaryVec) initializeDeprecatedMetric() { 138 v.SummaryOpts.markDeprecated() 139 v.initializeMetric() 140 } 141 142 // Default Prometheus Vec behavior is that member extraction results in creation of a new element 143 // if one with the unique label values is not found in the underlying stored metricMap. 144 // This means that if this function is called but the underlying metric is not registered 145 // (which means it will never be exposed externally nor consumed), the metric will exist in memory 146 // for perpetuity (i.e. throughout application lifecycle). 147 // 148 // For reference: https://github.com/prometheus/client_golang/blob/v0.9.2/prometheus/histogram.go#L460-L470 149 // 150 // In contrast, the Vec behavior in this package is that member extraction before registration 151 // returns a permanent noop object. 152 153 // WithLabelValues returns the ObserverMetric for the given slice of label 154 // values (same order as the VariableLabels in Desc). If that combination of 155 // label values is accessed for the first time, a new ObserverMetric is created IFF the summaryVec 156 // has been registered to a metrics registry. 157 func (v *SummaryVec) WithLabelValues(lvs ...string) ObserverMetric { 158 if !v.IsCreated() { 159 return noop 160 } 161 if v.LabelValueAllowLists != nil { 162 v.LabelValueAllowLists.ConstrainToAllowedList(v.originalLabels, lvs) 163 } 164 return v.SummaryVec.WithLabelValues(lvs...) 165 } 166 167 // With returns the ObserverMetric for the given Labels map (the label names 168 // must match those of the VariableLabels in Desc). If that label map is 169 // accessed for the first time, a new ObserverMetric is created IFF the summaryVec has 170 // been registered to a metrics registry. 171 func (v *SummaryVec) With(labels map[string]string) ObserverMetric { 172 if !v.IsCreated() { 173 return noop 174 } 175 if v.LabelValueAllowLists != nil { 176 v.LabelValueAllowLists.ConstrainLabelMap(labels) 177 } 178 return v.SummaryVec.With(labels) 179 } 180 181 // Delete deletes the metric where the variable labels are the same as those 182 // passed in as labels. It returns true if a metric was deleted. 183 // 184 // It is not an error if the number and names of the Labels are inconsistent 185 // with those of the VariableLabels in Desc. However, such inconsistent Labels 186 // can never match an actual metric, so the method will always return false in 187 // that case. 188 func (v *SummaryVec) Delete(labels map[string]string) bool { 189 if !v.IsCreated() { 190 return false // since we haven't created the metric, we haven't deleted a metric with the passed in values 191 } 192 return v.SummaryVec.Delete(labels) 193 } 194 195 // Reset deletes all metrics in this vector. 196 func (v *SummaryVec) Reset() { 197 if !v.IsCreated() { 198 return 199 } 200 201 v.SummaryVec.Reset() 202 } 203 204 // WithContext returns wrapped SummaryVec with context 205 func (v *SummaryVec) WithContext(ctx context.Context) *SummaryVecWithContext { 206 return &SummaryVecWithContext{ 207 ctx: ctx, 208 SummaryVec: v, 209 } 210 } 211 212 // SummaryVecWithContext is the wrapper of SummaryVec with context. 213 type SummaryVecWithContext struct { 214 *SummaryVec 215 ctx context.Context 216 } 217 218 // WithLabelValues is the wrapper of SummaryVec.WithLabelValues. 219 func (vc *SummaryVecWithContext) WithLabelValues(lvs ...string) ObserverMetric { 220 return vc.SummaryVec.WithLabelValues(lvs...) 221 } 222 223 // With is the wrapper of SummaryVec.With. 224 func (vc *SummaryVecWithContext) With(labels map[string]string) ObserverMetric { 225 return vc.SummaryVec.With(labels) 226 } 227