mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
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:
parent
1aa94165d9
commit
43afb85056
@ -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{
|
||||
|
@ -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
|
||||
|
@ -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"])
|
||||
})
|
||||
|
@ -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"`
|
||||
|
Loading…
Reference in New Issue
Block a user