@@ -143,7 +143,7 @@ type compiledMetric interface {
143143 Type () metric.Type
144144}
145145
146- // newCompiledMetric returns a compiledMetric depending given the metric type.
146+ // newCompiledMetric returns a compiledMetric depending on given the metric type.
147147func newCompiledMetric (m Metric ) (compiledMetric , error ) {
148148 switch m .Type {
149149 case MetricTypeGauge :
@@ -217,7 +217,40 @@ func (c *compiledGauge) Values(v interface{}) (result []eachValue, errs []error)
217217 switch iter := v .(type ) {
218218 case map [string ]interface {}:
219219 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+ }
221254 if err != nil {
222255 onError (fmt .Errorf ("[%s]: %w" , key , err ))
223256 continue
@@ -387,7 +420,19 @@ func less(a, b map[string]string) bool {
387420
388421func (c compiledGauge ) value (it interface {}) (* eachValue , error ) {
389422 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 )
391436 if err != nil {
392437 return nil , fmt .Errorf ("%s: %w" , c .ValueFrom , err )
393438 }
@@ -554,7 +599,10 @@ func compilePath(path []string) (out valuePath, _ error) {
554599 } else if s , ok := m .([]interface {}); ok {
555600 i , err := strconv .Atoi (part )
556601 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
558606 }
559607 if i < 0 {
560608 // negative index
0 commit comments