1
16
17 package metrics
18
19 import (
20 "fmt"
21 "os"
22 "path/filepath"
23 "strings"
24 "sync"
25 "time"
26
27 "github.com/prometheus/client_golang/prometheus"
28 "gopkg.in/yaml.v2"
29
30 "k8s.io/apimachinery/pkg/util/sets"
31 promext "k8s.io/component-base/metrics/prometheusextension"
32 "k8s.io/klog/v2"
33 )
34
35 var (
36 labelValueAllowLists = map[string]*MetricLabelAllowList{}
37 allowListLock sync.RWMutex
38 )
39
40
41
42
43
44
45
46 type KubeOpts struct {
47 Namespace string
48 Subsystem string
49 Name string
50 Help string
51 ConstLabels map[string]string
52 DeprecatedVersion string
53 deprecateOnce sync.Once
54 annotateOnce sync.Once
55 StabilityLevel StabilityLevel
56 LabelValueAllowLists *MetricLabelAllowList
57 }
58
59
60
61
62
63
64
65
66 func BuildFQName(namespace, subsystem, name string) string {
67 return prometheus.BuildFQName(namespace, subsystem, name)
68 }
69
70
71 type StabilityLevel string
72
73 const (
74
75
76 INTERNAL StabilityLevel = "INTERNAL"
77
78
79 ALPHA StabilityLevel = "ALPHA"
80
81
82 BETA StabilityLevel = "BETA"
83
84
85 STABLE StabilityLevel = "STABLE"
86 )
87
88
89 func (sl *StabilityLevel) setDefaults() {
90 switch *sl {
91 case "":
92 *sl = ALPHA
93 default:
94
95 }
96 }
97
98
99 type CounterOpts KubeOpts
100
101
102 func (o *CounterOpts) markDeprecated() {
103 o.deprecateOnce.Do(func() {
104 o.Help = fmt.Sprintf("(Deprecated since %v) %v", o.DeprecatedVersion, o.Help)
105 })
106 }
107
108
109
110 func (o *CounterOpts) annotateStabilityLevel() {
111 o.annotateOnce.Do(func() {
112 o.Help = fmt.Sprintf("[%v] %v", o.StabilityLevel, o.Help)
113 })
114 }
115
116
117
118 func (o *CounterOpts) toPromCounterOpts() prometheus.CounterOpts {
119 return prometheus.CounterOpts{
120 Namespace: o.Namespace,
121 Subsystem: o.Subsystem,
122 Name: o.Name,
123 Help: o.Help,
124 ConstLabels: o.ConstLabels,
125 }
126 }
127
128
129 type GaugeOpts KubeOpts
130
131
132 func (o *GaugeOpts) markDeprecated() {
133 o.deprecateOnce.Do(func() {
134 o.Help = fmt.Sprintf("(Deprecated since %v) %v", o.DeprecatedVersion, o.Help)
135 })
136 }
137
138
139
140 func (o *GaugeOpts) annotateStabilityLevel() {
141 o.annotateOnce.Do(func() {
142 o.Help = fmt.Sprintf("[%v] %v", o.StabilityLevel, o.Help)
143 })
144 }
145
146
147
148 func (o *GaugeOpts) toPromGaugeOpts() prometheus.GaugeOpts {
149 return prometheus.GaugeOpts{
150 Namespace: o.Namespace,
151 Subsystem: o.Subsystem,
152 Name: o.Name,
153 Help: o.Help,
154 ConstLabels: o.ConstLabels,
155 }
156 }
157
158
159
160
161
162 type HistogramOpts struct {
163 Namespace string
164 Subsystem string
165 Name string
166 Help string
167 ConstLabels map[string]string
168 Buckets []float64
169 DeprecatedVersion string
170 deprecateOnce sync.Once
171 annotateOnce sync.Once
172 StabilityLevel StabilityLevel
173 LabelValueAllowLists *MetricLabelAllowList
174 }
175
176
177 func (o *HistogramOpts) markDeprecated() {
178 o.deprecateOnce.Do(func() {
179 o.Help = fmt.Sprintf("(Deprecated since %v) %v", o.DeprecatedVersion, o.Help)
180 })
181 }
182
183
184
185 func (o *HistogramOpts) annotateStabilityLevel() {
186 o.annotateOnce.Do(func() {
187 o.Help = fmt.Sprintf("[%v] %v", o.StabilityLevel, o.Help)
188 })
189 }
190
191
192
193 func (o *HistogramOpts) toPromHistogramOpts() prometheus.HistogramOpts {
194 return prometheus.HistogramOpts{
195 Namespace: o.Namespace,
196 Subsystem: o.Subsystem,
197 Name: o.Name,
198 Help: o.Help,
199 ConstLabels: o.ConstLabels,
200 Buckets: o.Buckets,
201 }
202 }
203
204
205
206
207
208 type TimingHistogramOpts struct {
209 Namespace string
210 Subsystem string
211 Name string
212 Help string
213 ConstLabels map[string]string
214 Buckets []float64
215 InitialValue float64
216 DeprecatedVersion string
217 deprecateOnce sync.Once
218 annotateOnce sync.Once
219 StabilityLevel StabilityLevel
220 LabelValueAllowLists *MetricLabelAllowList
221 }
222
223
224 func (o *TimingHistogramOpts) markDeprecated() {
225 o.deprecateOnce.Do(func() {
226 o.Help = fmt.Sprintf("(Deprecated since %v) %v", o.DeprecatedVersion, o.Help)
227 })
228 }
229
230
231
232 func (o *TimingHistogramOpts) annotateStabilityLevel() {
233 o.annotateOnce.Do(func() {
234 o.Help = fmt.Sprintf("[%v] %v", o.StabilityLevel, o.Help)
235 })
236 }
237
238
239
240 func (o *TimingHistogramOpts) toPromHistogramOpts() promext.TimingHistogramOpts {
241 return promext.TimingHistogramOpts{
242 Namespace: o.Namespace,
243 Subsystem: o.Subsystem,
244 Name: o.Name,
245 Help: o.Help,
246 ConstLabels: o.ConstLabels,
247 Buckets: o.Buckets,
248 InitialValue: o.InitialValue,
249 }
250 }
251
252
253
254
255
256
257 type SummaryOpts struct {
258 Namespace string
259 Subsystem string
260 Name string
261 Help string
262 ConstLabels map[string]string
263 Objectives map[float64]float64
264 MaxAge time.Duration
265 AgeBuckets uint32
266 BufCap uint32
267 DeprecatedVersion string
268 deprecateOnce sync.Once
269 annotateOnce sync.Once
270 StabilityLevel StabilityLevel
271 LabelValueAllowLists *MetricLabelAllowList
272 }
273
274
275 func (o *SummaryOpts) markDeprecated() {
276 o.deprecateOnce.Do(func() {
277 o.Help = fmt.Sprintf("(Deprecated since %v) %v", o.DeprecatedVersion, o.Help)
278 })
279 }
280
281
282
283 func (o *SummaryOpts) annotateStabilityLevel() {
284 o.annotateOnce.Do(func() {
285 o.Help = fmt.Sprintf("[%v] %v", o.StabilityLevel, o.Help)
286 })
287 }
288
289
290
291 var (
292 defObjectives = map[float64]float64{0.5: 0.05, 0.9: 0.01, 0.99: 0.001}
293 )
294
295
296
297 func (o *SummaryOpts) toPromSummaryOpts() prometheus.SummaryOpts {
298
299
300 objectives := o.Objectives
301 if objectives == nil {
302 objectives = defObjectives
303 }
304 return prometheus.SummaryOpts{
305 Namespace: o.Namespace,
306 Subsystem: o.Subsystem,
307 Name: o.Name,
308 Help: o.Help,
309 ConstLabels: o.ConstLabels,
310 Objectives: objectives,
311 MaxAge: o.MaxAge,
312 AgeBuckets: o.AgeBuckets,
313 BufCap: o.BufCap,
314 }
315 }
316
317 type MetricLabelAllowList struct {
318 labelToAllowList map[string]sets.String
319 }
320
321 func (allowList *MetricLabelAllowList) ConstrainToAllowedList(labelNameList, labelValueList []string) {
322 for index, value := range labelValueList {
323 name := labelNameList[index]
324 if allowValues, ok := allowList.labelToAllowList[name]; ok {
325 if !allowValues.Has(value) {
326 labelValueList[index] = "unexpected"
327 cardinalityEnforcementUnexpectedCategorizationsTotal.Inc()
328 }
329 }
330 }
331 }
332
333 func (allowList *MetricLabelAllowList) ConstrainLabelMap(labels map[string]string) {
334 for name, value := range labels {
335 if allowValues, ok := allowList.labelToAllowList[name]; ok {
336 if !allowValues.Has(value) {
337 labels[name] = "unexpected"
338 cardinalityEnforcementUnexpectedCategorizationsTotal.Inc()
339 }
340 }
341 }
342 }
343
344 func SetLabelAllowListFromCLI(allowListMapping map[string]string) {
345 allowListLock.Lock()
346 defer allowListLock.Unlock()
347 for metricLabelName, labelValues := range allowListMapping {
348 metricName := strings.Split(metricLabelName, ",")[0]
349 labelName := strings.Split(metricLabelName, ",")[1]
350 valueSet := sets.NewString(strings.Split(labelValues, ",")...)
351
352 allowList, ok := labelValueAllowLists[metricName]
353 if ok {
354 allowList.labelToAllowList[labelName] = valueSet
355 } else {
356 labelToAllowList := make(map[string]sets.String)
357 labelToAllowList[labelName] = valueSet
358 labelValueAllowLists[metricName] = &MetricLabelAllowList{
359 labelToAllowList,
360 }
361 }
362 }
363 }
364
365 func SetLabelAllowListFromManifest(manifest string) {
366 allowListLock.Lock()
367 defer allowListLock.Unlock()
368 allowListMapping := make(map[string]string)
369 data, err := os.ReadFile(filepath.Clean(manifest))
370 if err != nil {
371 klog.Errorf("Failed to read allow list manifest: %v", err)
372 return
373 }
374 err = yaml.Unmarshal(data, &allowListMapping)
375 if err != nil {
376 klog.Errorf("Failed to parse allow list manifest: %v", err)
377 return
378 }
379 SetLabelAllowListFromCLI(allowListMapping)
380 }
381
View as plain text