diff --git a/pkg/tsdb/cloudmonitoring/time_series_filter.go b/pkg/tsdb/cloudmonitoring/time_series_filter.go index a3c11572efb..d394156ec11 100644 --- a/pkg/tsdb/cloudmonitoring/time_series_filter.go +++ b/pkg/tsdb/cloudmonitoring/time_series_filter.go @@ -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{ diff --git a/pkg/tsdb/cloudmonitoring/time_series_query.go b/pkg/tsdb/cloudmonitoring/time_series_query.go index 541d55067de..20965ea5145 100644 --- a/pkg/tsdb/cloudmonitoring/time_series_query.go +++ b/pkg/tsdb/cloudmonitoring/time_series_query.go @@ -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 diff --git a/pkg/tsdb/cloudmonitoring/time_series_query_test.go b/pkg/tsdb/cloudmonitoring/time_series_query_test.go index 025e8cde225..27d4ed79b9a 100644 --- a/pkg/tsdb/cloudmonitoring/time_series_query_test.go +++ b/pkg/tsdb/cloudmonitoring/time_series_query_test.go @@ -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"]) }) diff --git a/pkg/tsdb/cloudmonitoring/types.go b/pkg/tsdb/cloudmonitoring/types.go index 421de8d2746..d42613788a9 100644 --- a/pkg/tsdb/cloudmonitoring/types.go +++ b/pkg/tsdb/cloudmonitoring/types.go @@ -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"`