mirror of
https://github.com/grafana/grafana.git
synced 2025-02-11 08:05:43 -06:00
Prometheus: Transform NaN values from matrix response to null on backend (#40757)
* avoid duplicate allocations * set labels * Replace NaN in matrix with null * Refactor and add test * Update test * Append response only if no error Co-authored-by: Ryan McKinley <ryantxu@gmail.com>
This commit is contained in:
parent
967721068e
commit
ba90b57b66
@ -181,9 +181,9 @@ func (s *Service) QueryData(ctx context.Context, req *backend.QueryDataRequest)
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
plog.Error("Exemplar query", query.Expr, "failed with", err)
|
plog.Error("Exemplar query", query.Expr, "failed with", err)
|
||||||
result.Responses[query.RefId] = backend.DataResponse{Error: err}
|
result.Responses[query.RefId] = backend.DataResponse{Error: err}
|
||||||
continue
|
} else {
|
||||||
|
response[ExemplarQueryType] = exemplarResponse
|
||||||
}
|
}
|
||||||
response[ExemplarQueryType] = exemplarResponse
|
|
||||||
}
|
}
|
||||||
|
|
||||||
frames, err := parseResponse(response, query)
|
frames, err := parseResponse(response, query)
|
||||||
@ -403,19 +403,28 @@ func matrixToDataFrames(matrix model.Matrix, query *PrometheusQuery) data.Frames
|
|||||||
|
|
||||||
for _, v := range matrix {
|
for _, v := range matrix {
|
||||||
tags := make(map[string]string, len(v.Metric))
|
tags := make(map[string]string, len(v.Metric))
|
||||||
timeVector := make([]time.Time, 0, len(v.Values))
|
|
||||||
values := make([]float64, 0, len(v.Values))
|
|
||||||
for k, v := range v.Metric {
|
for k, v := range v.Metric {
|
||||||
tags[string(k)] = string(v)
|
tags[string(k)] = string(v)
|
||||||
}
|
}
|
||||||
for _, k := range v.Values {
|
|
||||||
timeVector = append(timeVector, time.Unix(k.Timestamp.Unix(), 0).UTC())
|
timeField := data.NewFieldFromFieldType(data.FieldTypeTime, len(v.Values))
|
||||||
values = append(values, float64(k.Value))
|
valueField := data.NewFieldFromFieldType(data.FieldTypeNullableFloat64, len(v.Values))
|
||||||
|
|
||||||
|
for i, k := range v.Values {
|
||||||
|
timeField.Set(i, time.Unix(k.Timestamp.Unix(), 0).UTC())
|
||||||
|
value := float64(k.Value)
|
||||||
|
if !math.IsNaN(value) {
|
||||||
|
valueField.Set(i, &value)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
name := formatLegend(v.Metric, query)
|
name := formatLegend(v.Metric, query)
|
||||||
frame := data.NewFrame(name,
|
timeField.Name = data.TimeSeriesTimeFieldName
|
||||||
data.NewField("Time", nil, timeVector),
|
valueField.Name = data.TimeSeriesValueFieldName
|
||||||
data.NewField("Value", tags, values).SetConfig(&data.FieldConfig{DisplayNameFromDS: name}))
|
valueField.Config = &data.FieldConfig{DisplayNameFromDS: name}
|
||||||
|
valueField.Labels = tags
|
||||||
|
|
||||||
|
frame := data.NewFrame(name, timeField, valueField)
|
||||||
frame.Meta = &data.FrameMeta{
|
frame.Meta = &data.FrameMeta{
|
||||||
Custom: map[string]string{
|
Custom: map[string]string{
|
||||||
"resultType": "matrix",
|
"resultType": "matrix",
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package prometheus
|
package prometheus
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"math"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -440,6 +441,27 @@ func TestPrometheus_parseResponse(t *testing.T) {
|
|||||||
require.Equal(t, "UTC", testValue.(time.Time).Location().String())
|
require.Equal(t, "UTC", testValue.(time.Time).Location().String())
|
||||||
})
|
})
|
||||||
|
|
||||||
|
t.Run("matrix response with NaN value should be changed to null", func(t *testing.T) {
|
||||||
|
value := make(map[PrometheusQueryType]interface{})
|
||||||
|
value[RangeQueryType] = p.Matrix{
|
||||||
|
&p.SampleStream{
|
||||||
|
Metric: p.Metric{"app": "Application"},
|
||||||
|
Values: []p.SamplePair{
|
||||||
|
{Value: p.SampleValue(math.NaN()), Timestamp: 1000},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
query := &PrometheusQuery{
|
||||||
|
LegendFormat: "",
|
||||||
|
}
|
||||||
|
res, err := parseResponse(value, query)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
var nilPointer *float64
|
||||||
|
require.Equal(t, res[0].Fields[1].Name, "Value")
|
||||||
|
require.Equal(t, res[0].Fields[1].At(0), nilPointer)
|
||||||
|
})
|
||||||
|
|
||||||
t.Run("vector response should be parsed normally", func(t *testing.T) {
|
t.Run("vector response should be parsed normally", func(t *testing.T) {
|
||||||
value := make(map[PrometheusQueryType]interface{})
|
value := make(map[PrometheusQueryType]interface{})
|
||||||
value[RangeQueryType] = p.Vector{
|
value[RangeQueryType] = p.Vector{
|
||||||
|
Loading…
Reference in New Issue
Block a user