@@ -67,12 +67,22 @@ type CSIMetricsManager interface {
67
67
// operationName - Name of the CSI operation.
68
68
// operationErr - Error, if any, that resulted from execution of operation.
69
69
// operationDuration - time it took for the operation to complete
70
- // labelValues - for each additional label that was defined in WithLabels(), one value has to be passed (usually none)
70
+ //
71
+ // If WithLabelNames was used to define additional labels when constructing
72
+ // the manager, then WithLabelValues should be used to create a wrapper which
73
+ // holds the corresponding values before calling RecordMetrics of the wrapper.
74
+ // Labels with missing values are recorded as empty.
71
75
RecordMetrics (
72
76
operationName string ,
73
77
operationErr error ,
74
- operationDuration time.Duration ,
75
- labelValues ... string )
78
+ operationDuration time.Duration )
79
+
80
+ // WithLabelValues must be used to add the additional label
81
+ // values defined via WithLabelNames. When calling RecordMetrics
82
+ // without it or with too few values, the missing values are
83
+ // recorded as empty. WithLabelValues can be called multiple times
84
+ // and then accumulates values.
85
+ WithLabelValues (labels map [string ]string ) (CSIMetricsManager , error )
76
86
77
87
// SetDriverName is called to update the CSI driver name. This should be done
78
88
// as soon as possible, otherwise metrics recorded by this manager will be
@@ -108,6 +118,9 @@ func WithStabilityLevel(stabilityLevel metrics.StabilityLevel) MetricsManagerOpt
108
118
// default labels (driver, method call, and gRPC result). This makes
109
119
// it possible to partition the histograms along additional
110
120
// dimensions.
121
+ //
122
+ // To record a metrics with additional values, use
123
+ // CSIMetricManager.WithLabelValues().RecordMetrics().
111
124
func WithLabelNames (labelNames ... string ) MetricsManagerOption {
112
125
return func (cmm * csiMetricsManager ) {
113
126
cmm .additionalLabelNames = labelNames
@@ -208,25 +221,91 @@ func (cmm *csiMetricsManager) GetRegistry() metrics.KubeRegistry {
208
221
return cmm .registry
209
222
}
210
223
211
- // RecordMetrics must be called upon CSI Operation completion to record
212
- // the operation's metric.
213
- // operationName - Name of the CSI operation.
214
- // operationErr - Error, if any, that resulted from execution of operation.
215
- // operationDuration - time it took for the operation to complete
216
- // labelValues - for each additional label that was defined in WithLabels(), one value has to be passed (usually none)
224
+ // RecordMetrics implements CSIMetricsManager.RecordMetrics.
217
225
func (cmm * csiMetricsManager ) RecordMetrics (
226
+ operationName string ,
227
+ operationErr error ,
228
+ operationDuration time.Duration ) {
229
+ cmm .recordMetricsWithLabels (operationName , operationErr , operationDuration )
230
+ }
231
+
232
+ // recordMetricsWithLabels is the internal implementation of RecordMetrics.
233
+ func (cmm * csiMetricsManager ) recordMetricsWithLabels (
218
234
operationName string ,
219
235
operationErr error ,
220
236
operationDuration time.Duration ,
221
237
labelValues ... string ) {
222
238
values := []string {cmm .driverName , operationName , getErrorCode (operationErr )}
223
- values = append (values , labelValues ... )
239
+ toAdd := len (labelValues )
240
+ if toAdd > len (cmm .additionalLabelNames ) {
241
+ // To many labels?! Truncate. Shouldn't happen because of
242
+ // error checking in WithLabelValues.
243
+ toAdd = len (cmm .additionalLabelNames )
244
+ }
245
+ values = append (values , labelValues [0 :toAdd ]... )
246
+ for i := toAdd ; i < len (cmm .additionalLabelNames ); i ++ {
247
+ // Backfill missing values with empty string.
248
+ values = append (values , "" )
249
+ }
224
250
for _ , label := range cmm .additionalLabels {
225
251
values = append (values , label .value )
226
252
}
227
253
cmm .csiOperationsLatencyMetric .WithLabelValues (values ... ).Observe (operationDuration .Seconds ())
228
254
}
229
255
256
+ type csiMetricsManagerWithValues struct {
257
+ * csiMetricsManager
258
+
259
+ // additionalValues holds the values passed via WithLabelValues.
260
+ additionalValues []string
261
+ }
262
+
263
+ // WithLabelValues in the base metrics manager creates a fresh wrapper with no labels and let's
264
+ // that deal with adding the label values.
265
+ func (cmm * csiMetricsManager ) WithLabelValues (labels map [string ]string ) (CSIMetricsManager , error ) {
266
+ cmmv := & csiMetricsManagerWithValues {csiMetricsManager : cmm }
267
+ return cmmv .WithLabelValues (labels )
268
+ }
269
+
270
+ // WithLabelValues in the wrapper creates a wrapper which has all existing labels and
271
+ // adds the new ones, with error checking.
272
+ func (cmmv * csiMetricsManagerWithValues ) WithLabelValues (labels map [string ]string ) (CSIMetricsManager , error ) {
273
+ extended := & csiMetricsManagerWithValues {cmmv .csiMetricsManager , append ([]string {}, cmmv .additionalValues ... )}
274
+
275
+ for name := range labels {
276
+ if ! cmmv .haveAdditionalLabel (name ) {
277
+ return nil , fmt .Errorf ("label %q was not defined via WithLabelNames" , name )
278
+ }
279
+ }
280
+ // Add in same order as in the label definition.
281
+ for _ , name := range cmmv .additionalLabelNames {
282
+ if value , ok := labels [name ]; ok {
283
+ if len (cmmv .additionalValues ) >= len (extended .additionalLabelNames ) {
284
+ return nil , fmt .Errorf ("label %q = %q cannot be added, all labels already have values %v" , name , value , cmmv .additionalValues )
285
+ }
286
+ extended .additionalValues = append (extended .additionalValues , value )
287
+ }
288
+ }
289
+ return extended , nil
290
+ }
291
+
292
+ func (cmm * csiMetricsManager ) haveAdditionalLabel (name string ) bool {
293
+ for _ , n := range cmm .additionalLabelNames {
294
+ if n == name {
295
+ return true
296
+ }
297
+ }
298
+ return false
299
+ }
300
+
301
+ // RecordMetrics passes the stored values as to the implementation.
302
+ func (cmmv * csiMetricsManagerWithValues ) RecordMetrics (
303
+ operationName string ,
304
+ operationErr error ,
305
+ operationDuration time.Duration ) {
306
+ cmmv .recordMetricsWithLabels (operationName , operationErr , operationDuration , cmmv .additionalValues ... )
307
+ }
308
+
230
309
// SetDriverName is called to update the CSI driver name. This should be done
231
310
// as soon as possible, otherwise metrics recorded by this manager will be
232
311
// recorded with an "unknown-driver" driver_name.
0 commit comments