@@ -67,12 +67,22 @@ type CSIMetricsManager interface {
6767 // operationName - Name of the CSI operation.
6868 // operationErr - Error, if any, that resulted from execution of operation.
6969 // 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.
7175 RecordMetrics (
7276 operationName string ,
7377 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 )
7686
7787 // SetDriverName is called to update the CSI driver name. This should be done
7888 // as soon as possible, otherwise metrics recorded by this manager will be
@@ -108,6 +118,9 @@ func WithStabilityLevel(stabilityLevel metrics.StabilityLevel) MetricsManagerOpt
108118// default labels (driver, method call, and gRPC result). This makes
109119// it possible to partition the histograms along additional
110120// dimensions.
121+ //
122+ // To record a metrics with additional values, use
123+ // CSIMetricManager.WithLabelValues().RecordMetrics().
111124func WithLabelNames (labelNames ... string ) MetricsManagerOption {
112125 return func (cmm * csiMetricsManager ) {
113126 cmm .additionalLabelNames = labelNames
@@ -208,25 +221,91 @@ func (cmm *csiMetricsManager) GetRegistry() metrics.KubeRegistry {
208221 return cmm .registry
209222}
210223
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.
217225func (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 (
218234 operationName string ,
219235 operationErr error ,
220236 operationDuration time.Duration ,
221237 labelValues ... string ) {
222238 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+ }
224250 for _ , label := range cmm .additionalLabels {
225251 values = append (values , label .value )
226252 }
227253 cmm .csiOperationsLatencyMetric .WithLabelValues (values ... ).Observe (operationDuration .Seconds ())
228254}
229255
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+
230309// SetDriverName is called to update the CSI driver name. This should be done
231310// as soon as possible, otherwise metrics recorded by this manager will be
232311// recorded with an "unknown-driver" driver_name.
0 commit comments