@@ -143,7 +143,7 @@ type compiledMetric interface {
143
143
Type () metric.Type
144
144
}
145
145
146
- // newCompiledMetric returns a compiledMetric depending given the metric type.
146
+ // newCompiledMetric returns a compiledMetric depending on given the metric type.
147
147
func newCompiledMetric (m Metric ) (compiledMetric , error ) {
148
148
switch m .Type {
149
149
case MetricTypeGauge :
@@ -217,7 +217,40 @@ func (c *compiledGauge) Values(v interface{}) (result []eachValue, errs []error)
217
217
switch iter := v .(type ) {
218
218
case map [string ]interface {}:
219
219
for key , it := range iter {
220
- ev , err := c .value (it )
220
+ // Try to deduce `valueFrom`'s value from the current element.
221
+ var ev * eachValue
222
+ var err error
223
+ var didResolveValueFrom bool
224
+ // `valueFrom` will ultimately be rendered into a string and sent to the fallback in place, which also expects a string.
225
+ // So we'll do the same and operate on the string representation of `valueFrom`'s value.
226
+ sValueFrom := c .ValueFrom .String ()
227
+ // No comma means we're looking at a unit-length path (in an array).
228
+ if ! strings .Contains (sValueFrom , "," ) &&
229
+ sValueFrom [0 ] == '[' &&
230
+ // "[...]" and not "[]".
231
+ len (sValueFrom ) > 2 {
232
+ extractedValueFrom := sValueFrom [1 : len (sValueFrom )- 1 ]
233
+ if key == extractedValueFrom {
234
+ gotFloat , err := toFloat64 (it , c .NilIsZero )
235
+ if err != nil {
236
+ onError (fmt .Errorf ("[%s]: %w" , key , err ))
237
+ continue
238
+ }
239
+ labels := make (map [string ]string )
240
+ ev = & eachValue {
241
+ Labels : labels ,
242
+ Value : gotFloat ,
243
+ }
244
+ didResolveValueFrom = true
245
+ }
246
+ }
247
+ // Fallback to the regular path resolution, if we didn't manage to resolve `valueFrom`'s value.
248
+ if ! didResolveValueFrom {
249
+ ev , err = c .value (it )
250
+ if ev == nil {
251
+ continue
252
+ }
253
+ }
221
254
if err != nil {
222
255
onError (fmt .Errorf ("[%s]: %w" , key , err ))
223
256
continue
@@ -387,7 +420,19 @@ func less(a, b map[string]string) bool {
387
420
388
421
func (c compiledGauge ) value (it interface {}) (* eachValue , error ) {
389
422
labels := make (map [string ]string )
390
- value , err := toFloat64 (c .ValueFrom .Get (it ), c .NilIsZero )
423
+ got := c .ValueFrom .Get (it )
424
+ // If `valueFrom` was not resolved, respect `NilIsZero` and return.
425
+ if got == nil {
426
+ if c .NilIsZero {
427
+ return & eachValue {
428
+ Labels : labels ,
429
+ Value : 0 ,
430
+ }, nil
431
+ }
432
+ // Don't error if there was not a type-casting issue (`toFloat64`), but rather a failed lookup.
433
+ return nil , nil
434
+ }
435
+ value , err := toFloat64 (got , c .NilIsZero )
391
436
if err != nil {
392
437
return nil , fmt .Errorf ("%s: %w" , c .ValueFrom , err )
393
438
}
@@ -554,7 +599,10 @@ func compilePath(path []string) (out valuePath, _ error) {
554
599
} else if s , ok := m .([]interface {}); ok {
555
600
i , err := strconv .Atoi (part )
556
601
if err != nil {
557
- return fmt .Errorf ("invalid list index: %s" , part )
602
+ // This means we are here: [ <string>, <int>, ... ] (eg., [ "foo", "0", ... ], i.e., <path>.foo[0]...
603
+ // ^
604
+ // Skip over.
605
+ return nil
558
606
}
559
607
if i < 0 {
560
608
// negative index
0 commit comments