diff --git a/pkg/tsdb/prometheus/prometheus.go b/pkg/tsdb/prometheus/prometheus.go index 32f4fc890d2..6043861773e 100644 --- a/pkg/tsdb/prometheus/prometheus.go +++ b/pkg/tsdb/prometheus/prometheus.go @@ -181,9 +181,9 @@ func (s *Service) QueryData(ctx context.Context, req *backend.QueryDataRequest) if err != nil { plog.Error("Exemplar query", query.Expr, "failed with", err) result.Responses[query.RefId] = backend.DataResponse{Error: err} - continue + } else { + response[ExemplarQueryType] = exemplarResponse } - response[ExemplarQueryType] = exemplarResponse } frames, err := parseResponse(response, query) @@ -403,19 +403,28 @@ func matrixToDataFrames(matrix model.Matrix, query *PrometheusQuery) data.Frames for _, v := range matrix { 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 { tags[string(k)] = string(v) } - for _, k := range v.Values { - timeVector = append(timeVector, time.Unix(k.Timestamp.Unix(), 0).UTC()) - values = append(values, float64(k.Value)) + + timeField := data.NewFieldFromFieldType(data.FieldTypeTime, len(v.Values)) + 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) - frame := data.NewFrame(name, - data.NewField("Time", nil, timeVector), - data.NewField("Value", tags, values).SetConfig(&data.FieldConfig{DisplayNameFromDS: name})) + timeField.Name = data.TimeSeriesTimeFieldName + valueField.Name = data.TimeSeriesValueFieldName + valueField.Config = &data.FieldConfig{DisplayNameFromDS: name} + valueField.Labels = tags + + frame := data.NewFrame(name, timeField, valueField) frame.Meta = &data.FrameMeta{ Custom: map[string]string{ "resultType": "matrix", diff --git a/pkg/tsdb/prometheus/prometheus_test.go b/pkg/tsdb/prometheus/prometheus_test.go index 0d91b89b1d2..e3625618c2a 100644 --- a/pkg/tsdb/prometheus/prometheus_test.go +++ b/pkg/tsdb/prometheus/prometheus_test.go @@ -1,6 +1,7 @@ package prometheus import ( + "math" "testing" "time" @@ -440,6 +441,27 @@ func TestPrometheus_parseResponse(t *testing.T) { 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) { value := make(map[PrometheusQueryType]interface{}) value[RangeQueryType] = p.Vector{