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"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/url"
|
"net/url"
|
||||||
"strconv"
|
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"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)
|
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,
|
func parseTimeSeriesResponse(queryRes *backend.DataResponse,
|
||||||
response cloudMonitoringResponse, executedQueryString string, query cloudMonitoringQueryExecutor, params url.Values, groupBys []string) error {
|
response cloudMonitoringResponse, executedQueryString string, query cloudMonitoringQueryExecutor, params url.Values, groupBys []string) error {
|
||||||
frames := data.Frames{}
|
frames := data.Frames{}
|
||||||
|
|
||||||
for _, series := range response.TimeSeries {
|
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 := data.NewFrameOfFieldTypes("", len(series.Points), data.FieldTypeTime, data.FieldTypeFloat64)
|
||||||
frame.RefID = query.getRefID()
|
frame.RefID = query.getRefID()
|
||||||
frame.Meta = &data.FrameMeta{
|
frame.Meta = &data.FrameMeta{
|
||||||
|
@ -3,13 +3,11 @@ package cloudmonitoring
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strconv"
|
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
||||||
"github.com/grafana/grafana-plugin-sdk-go/data"
|
"github.com/grafana/grafana-plugin-sdk-go/data"
|
||||||
"github.com/huandu/xstrings"
|
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/infra/tracing"
|
"github.com/grafana/grafana/pkg/infra/tracing"
|
||||||
"github.com/grafana/grafana/pkg/tsdb/intervalv2"
|
"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)
|
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,
|
func (timeSeriesQuery *cloudMonitoringTimeSeriesQuery) parseResponse(queryRes *backend.DataResponse,
|
||||||
response cloudMonitoringResponse, executedQueryString string) error {
|
response cloudMonitoringResponse, executedQueryString string) error {
|
||||||
frames := data.Frames{}
|
frames := data.Frames{}
|
||||||
@ -69,7 +47,7 @@ func (timeSeriesQuery *cloudMonitoringTimeSeriesQuery) parseResponse(queryRes *b
|
|||||||
for _, series := range response.TimeSeriesData {
|
for _, series := range response.TimeSeriesData {
|
||||||
frame := data.NewFrameOfFieldTypes("", len(series.PointData), data.FieldTypeTime, data.FieldTypeFloat64)
|
frame := data.NewFrameOfFieldTypes("", len(series.PointData), data.FieldTypeTime, data.FieldTypeFloat64)
|
||||||
frame.RefID = timeSeriesQuery.refID
|
frame.RefID = timeSeriesQuery.refID
|
||||||
seriesLabels := extractTimeSeriesDataLabels(response, series)
|
seriesLabels, defaultMetricName := series.getLabels(response.TimeSeriesDescriptor.LabelDescriptors)
|
||||||
|
|
||||||
for n, d := range response.TimeSeriesDescriptor.PointDescriptors {
|
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.
|
// 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
|
seriesLabels["metric.name"] = d.Key
|
||||||
defaultMetricName := d.Key
|
|
||||||
|
|
||||||
customFrameMeta := map[string]interface{}{}
|
customFrameMeta := map[string]interface{}{}
|
||||||
customFrameMeta["labels"] = seriesLabels
|
customFrameMeta["labels"] = seriesLabels
|
||||||
|
@ -5,6 +5,8 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
"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/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
@ -32,7 +34,7 @@ func TestTimeSeriesQuery(t *testing.T) {
|
|||||||
}
|
}
|
||||||
err = query.parseResponse(res, data, "")
|
err = query.parseResponse(res, data, "")
|
||||||
frames := res.Frames
|
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))
|
assert.Equal(t, 843302441.9, frames[0].Fields[1].At(0))
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -105,7 +107,7 @@ func TestTimeSeriesQuery(t *testing.T) {
|
|||||||
frames := res.Frames
|
frames := res.Frames
|
||||||
custom, ok := frames[0].Meta.Custom.(map[string]interface{})
|
custom, ok := frames[0].Meta.Custom.(map[string]interface{})
|
||||||
require.True(t, ok)
|
require.True(t, ok)
|
||||||
labels, ok := custom["labels"].(map[string]string)
|
labels, ok := custom["labels"].(gdata.Labels)
|
||||||
require.True(t, ok)
|
require.True(t, ok)
|
||||||
assert.Equal(t, "6724404429462225363", labels["resource.label.instance_id"])
|
assert.Equal(t, "6724404429462225363", labels["resource.label.instance_id"])
|
||||||
})
|
})
|
||||||
|
@ -2,10 +2,15 @@ package cloudmonitoring
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
"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/log"
|
||||||
"github.com/grafana/grafana/pkg/infra/tracing"
|
"github.com/grafana/grafana/pkg/infra/tracing"
|
||||||
@ -139,12 +144,14 @@ type point interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type timeSeriesDescriptor struct {
|
type timeSeriesDescriptor struct {
|
||||||
LabelDescriptors []struct {
|
LabelDescriptors []LabelDescriptor `json:"labelDescriptors"`
|
||||||
|
PointDescriptors []timeSeriesPointDescriptor `json:"pointDescriptors"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type LabelDescriptor struct {
|
||||||
Key string `json:"key"`
|
Key string `json:"key"`
|
||||||
ValueType string `json:"valueType"`
|
ValueType string `json:"valueType"`
|
||||||
Description string `json:"description"`
|
Description string `json:"description"`
|
||||||
} `json:"labelDescriptors"`
|
|
||||||
PointDescriptors []timeSeriesPointDescriptor `json:"pointDescriptors"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type timeSeriesPointDescriptor struct {
|
type timeSeriesPointDescriptor struct {
|
||||||
@ -178,6 +185,35 @@ func (ts timeSeriesData) getPoint(index int) point {
|
|||||||
return &ts.PointData[index]
|
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 {
|
type timeSeriesDataIterator struct {
|
||||||
timeSeriesData
|
timeSeriesData
|
||||||
timeSeriesPointDescriptor
|
timeSeriesPointDescriptor
|
||||||
@ -250,6 +286,56 @@ func (ts timeSeries) valueType() string {
|
|||||||
return ts.ValueType
|
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 {
|
type timeSeriesPoint struct {
|
||||||
Interval struct {
|
Interval struct {
|
||||||
StartTime time.Time `json:"startTime"`
|
StartTime time.Time `json:"startTime"`
|
||||||
|
Loading…
Reference in New Issue
Block a user