CloudMonitor: Improve detail of MQL series labels (#59747)

* Improve consistency between MQL and builder queries

* Review

* Review and fix test
This commit is contained in:
Andreas Christou 2022-12-07 15:32:54 +00:00 committed by GitHub
parent 1aa94165d9
commit 43afb85056
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 97 additions and 83 deletions

View File

@ -4,7 +4,6 @@ import (
"context"
"fmt"
"net/url"
"strconv"
"strings"
"time"
@ -20,62 +19,12 @@ func (timeSeriesFilter *cloudMonitoringTimeSeriesList) run(ctx context.Context,
return runTimeSeriesRequest(ctx, timeSeriesFilter.logger, req, s, dsInfo, tracer, timeSeriesFilter.parameters.ProjectName, timeSeriesFilter.params, nil)
}
func extractTimeSeriesLabels(series timeSeries, groupBys []string) (data.Labels, string) {
seriesLabels := data.Labels{}
defaultMetricName := series.Metric.Type
seriesLabels["resource.type"] = series.Resource.Type
groupBysMap := make(map[string]bool)
for _, groupBy := range groupBys {
groupBysMap[groupBy] = true
}
for key, value := range series.Metric.Labels {
seriesLabels["metric.label."+key] = value
if len(groupBys) == 0 || groupBysMap["metric.label."+key] {
defaultMetricName += " " + value
}
}
for key, value := range series.Resource.Labels {
seriesLabels["resource.label."+key] = value
if groupBysMap["resource.label."+key] {
defaultMetricName += " " + value
}
}
for labelType, labelTypeValues := range series.MetaData {
for labelKey, labelValue := range labelTypeValues {
key := xstrings.ToSnakeCase(fmt.Sprintf("metadata.%s.%s", labelType, labelKey))
switch v := labelValue.(type) {
case string:
seriesLabels[key] = v
case bool:
strVal := strconv.FormatBool(v)
seriesLabels[key] = strVal
case []interface{}:
for _, v := range v {
strVal := v.(string)
if len(seriesLabels[key]) > 0 {
strVal = fmt.Sprintf("%s, %s", seriesLabels[key], strVal)
}
seriesLabels[key] = strVal
}
}
}
}
return seriesLabels, defaultMetricName
}
func parseTimeSeriesResponse(queryRes *backend.DataResponse,
response cloudMonitoringResponse, executedQueryString string, query cloudMonitoringQueryExecutor, params url.Values, groupBys []string) error {
frames := data.Frames{}
for _, series := range response.TimeSeries {
seriesLabels, defaultMetricName := extractTimeSeriesLabels(series, groupBys)
seriesLabels, defaultMetricName := series.getLabels(groupBys)
frame := data.NewFrameOfFieldTypes("", len(series.Points), data.FieldTypeTime, data.FieldTypeFloat64)
frame.RefID = query.getRefID()
frame.Meta = &data.FrameMeta{

View File

@ -3,13 +3,11 @@ package cloudmonitoring
import (
"context"
"fmt"
"strconv"
"strings"
"time"
"github.com/grafana/grafana-plugin-sdk-go/backend"
"github.com/grafana/grafana-plugin-sdk-go/data"
"github.com/huandu/xstrings"
"github.com/grafana/grafana/pkg/infra/tracing"
"github.com/grafana/grafana/pkg/tsdb/intervalv2"
@ -42,26 +40,6 @@ func (timeSeriesQuery *cloudMonitoringTimeSeriesQuery) run(ctx context.Context,
return runTimeSeriesRequest(ctx, timeSeriesQuery.logger, req, s, dsInfo, tracer, timeSeriesQuery.parameters.ProjectName, nil, requestBody)
}
func extractTimeSeriesDataLabels(response cloudMonitoringResponse, series timeSeriesData) map[string]string {
seriesLabels := make(map[string]string)
for n, d := range response.TimeSeriesDescriptor.LabelDescriptors {
key := xstrings.ToSnakeCase(d.Key)
key = strings.Replace(key, ".", ".label.", 1)
labelValue := series.LabelValues[n]
switch d.ValueType {
case "BOOL":
strVal := strconv.FormatBool(labelValue.BoolValue)
seriesLabels[key] = strVal
case "INT64":
seriesLabels[key] = labelValue.Int64Value
default:
seriesLabels[key] = labelValue.StringValue
}
}
return seriesLabels
}
func (timeSeriesQuery *cloudMonitoringTimeSeriesQuery) parseResponse(queryRes *backend.DataResponse,
response cloudMonitoringResponse, executedQueryString string) error {
frames := data.Frames{}
@ -69,7 +47,7 @@ func (timeSeriesQuery *cloudMonitoringTimeSeriesQuery) parseResponse(queryRes *b
for _, series := range response.TimeSeriesData {
frame := data.NewFrameOfFieldTypes("", len(series.PointData), data.FieldTypeTime, data.FieldTypeFloat64)
frame.RefID = timeSeriesQuery.refID
seriesLabels := extractTimeSeriesDataLabels(response, series)
seriesLabels, defaultMetricName := series.getLabels(response.TimeSeriesDescriptor.LabelDescriptors)
for n, d := range response.TimeSeriesDescriptor.PointDescriptors {
// If more than 1 pointdescriptor was returned, three aggregations are returned per time series - min, mean and max.
@ -81,7 +59,6 @@ func (timeSeriesQuery *cloudMonitoringTimeSeriesQuery) parseResponse(queryRes *b
}
seriesLabels["metric.name"] = d.Key
defaultMetricName := d.Key
customFrameMeta := map[string]interface{}{}
customFrameMeta["labels"] = seriesLabels

View File

@ -5,6 +5,8 @@ import (
"time"
"github.com/grafana/grafana-plugin-sdk-go/backend"
gdata "github.com/grafana/grafana-plugin-sdk-go/data"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
@ -32,7 +34,7 @@ func TestTimeSeriesQuery(t *testing.T) {
}
err = query.parseResponse(res, data, "")
frames := res.Frames
assert.Equal(t, "value.usage.mean", frames[0].Fields[1].Name)
assert.Equal(t, "grafana-prod asia-northeast1-c 6724404429462225363 200", frames[0].Fields[1].Name)
assert.Equal(t, 843302441.9, frames[0].Fields[1].At(0))
})
@ -105,7 +107,7 @@ func TestTimeSeriesQuery(t *testing.T) {
frames := res.Frames
custom, ok := frames[0].Meta.Custom.(map[string]interface{})
require.True(t, ok)
labels, ok := custom["labels"].(map[string]string)
labels, ok := custom["labels"].(gdata.Labels)
require.True(t, ok)
assert.Equal(t, "6724404429462225363", labels["resource.label.instance_id"])
})

View File

@ -2,10 +2,15 @@ package cloudmonitoring
import (
"context"
"fmt"
"net/url"
"strconv"
"strings"
"time"
"github.com/grafana/grafana-plugin-sdk-go/backend"
"github.com/grafana/grafana-plugin-sdk-go/data"
"github.com/huandu/xstrings"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/infra/tracing"
@ -139,14 +144,16 @@ type point interface {
}
type timeSeriesDescriptor struct {
LabelDescriptors []struct {
Key string `json:"key"`
ValueType string `json:"valueType"`
Description string `json:"description"`
} `json:"labelDescriptors"`
LabelDescriptors []LabelDescriptor `json:"labelDescriptors"`
PointDescriptors []timeSeriesPointDescriptor `json:"pointDescriptors"`
}
type LabelDescriptor struct {
Key string `json:"key"`
ValueType string `json:"valueType"`
Description string `json:"description"`
}
type timeSeriesPointDescriptor struct {
Key string `json:"key"`
ValueType string `json:"valueType"`
@ -178,6 +185,35 @@ func (ts timeSeriesData) getPoint(index int) point {
return &ts.PointData[index]
}
func (ts timeSeriesData) getLabels(labelDescriptors []LabelDescriptor) (data.Labels, string) {
seriesLabels := make(map[string]string)
defaultMetricName := ""
for n, d := range labelDescriptors {
key := xstrings.ToSnakeCase(d.Key)
key = strings.Replace(key, ".", ".label.", 1)
labelValue := ts.LabelValues[n]
switch d.ValueType {
case "BOOL":
strVal := strconv.FormatBool(labelValue.BoolValue)
seriesLabels[key] = strVal
case "INT64":
seriesLabels[key] = labelValue.Int64Value
default:
seriesLabels[key] = labelValue.StringValue
}
if strings.Contains(key, "metric.label") || strings.Contains(key, "resource.label") {
defaultMetricName += seriesLabels[key] + " "
}
}
defaultMetricName = strings.Trim(defaultMetricName, " ")
return seriesLabels, defaultMetricName
}
type timeSeriesDataIterator struct {
timeSeriesData
timeSeriesPointDescriptor
@ -250,6 +286,56 @@ func (ts timeSeries) valueType() string {
return ts.ValueType
}
func (ts timeSeries) getLabels(groupBys []string) (data.Labels, string) {
seriesLabels := data.Labels{}
defaultMetricName := ts.Metric.Type
seriesLabels["resource.type"] = ts.Resource.Type
groupBysMap := make(map[string]bool)
for _, groupBy := range groupBys {
groupBysMap[groupBy] = true
}
for key, value := range ts.Metric.Labels {
seriesLabels["metric.label."+key] = value
if len(groupBys) == 0 || groupBysMap["metric.label."+key] {
defaultMetricName += " " + value
}
}
for key, value := range ts.Resource.Labels {
seriesLabels["resource.label."+key] = value
if groupBysMap["resource.label."+key] {
defaultMetricName += " " + value
}
}
for labelType, labelTypeValues := range ts.MetaData {
for labelKey, labelValue := range labelTypeValues {
key := xstrings.ToSnakeCase(fmt.Sprintf("metadata.%s.%s", labelType, labelKey))
switch v := labelValue.(type) {
case string:
seriesLabels[key] = v
case bool:
strVal := strconv.FormatBool(v)
seriesLabels[key] = strVal
case []interface{}:
for _, v := range v {
strVal := v.(string)
if len(seriesLabels[key]) > 0 {
strVal = fmt.Sprintf("%s, %s", seriesLabels[key], strVal)
}
seriesLabels[key] = strVal
}
}
}
}
return seriesLabels, defaultMetricName
}
type timeSeriesPoint struct {
Interval struct {
StartTime time.Time `json:"startTime"`