From d20c7260b3f7c6128460759b83b9ce7d85f81679 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Wed, 24 Oct 2018 11:18:49 +0200 Subject: [PATCH] Resource type filter (#13784) * stackdriver: add resource type to filter and group bys * stackdriver: remove not used param * stackdriver: refactor filter and group by code * stackdriver: remove resource type if its already in filter list * stackdriver: remove debug logging * stackdriver: remove more debug logging * stackdriver: append resource type to legend name if there are more than one type present in the response * stackdriver: only make new request if filter has real value * stackdriver: format legend support for resource type * stackdriver: add resource type to documentation * stackdriver: not returning promise from query function * stackdriver: fix refactoring bug * stackdriver: remove not used import (cherry picked from commit c5af0bf1c5f625c5f1ba13781ff89d53f1724a85) --- .../features/datasources/stackdriver.md | 10 +++ pkg/tsdb/stackdriver/stackdriver.go | 23 ++++- .../datasource/stackdriver/datasource.ts | 6 +- .../datasource/stackdriver/filter_segments.ts | 2 +- .../stackdriver/partials/query.filter.html | 2 +- .../datasource/stackdriver/query_ctrl.ts | 1 - .../stackdriver/query_filter_ctrl.ts | 88 ++++++++++++------- 7 files changed, 92 insertions(+), 40 deletions(-) diff --git a/docs/sources/features/datasources/stackdriver.md b/docs/sources/features/datasources/stackdriver.md index c525130aebb..d7091b7ece7 100644 --- a/docs/sources/features/datasources/stackdriver.md +++ b/docs/sources/features/datasources/stackdriver.md @@ -134,6 +134,16 @@ Example Alias By: `{{metric.type}} - {{metric.labels.instance_name}}` Example Result: `compute.googleapis.com/instance/cpu/usage_time - server1-prod` +It is also possible to resolve the name of the Monitored Resource Type. + +| Alias Pattern Format | Description | Example Result | +| ------------------------ | ------------------------------------------------| ---------------- | +| `{{resource.type}}` | returns the name of the monitored resource type | `gce_instance` | + +Example Alias By: `{{resource.type}} - {{metric.type}}` + +Example Result: `gce_instance - compute.googleapis.com/instance/cpu/usage_time` + ## Templating Instead of hard-coding things like server, application and sensor name in you metric queries you can use variables in their place. diff --git a/pkg/tsdb/stackdriver/stackdriver.go b/pkg/tsdb/stackdriver/stackdriver.go index 96242dfdec4..1a9bb93fd3d 100644 --- a/pkg/tsdb/stackdriver/stackdriver.go +++ b/pkg/tsdb/stackdriver/stackdriver.go @@ -337,11 +337,21 @@ func (e *StackdriverExecutor) unmarshalResponse(res *http.Response) (Stackdriver func (e *StackdriverExecutor) parseResponse(queryRes *tsdb.QueryResult, data StackdriverResponse, query *StackdriverQuery) error { metricLabels := make(map[string][]string) resourceLabels := make(map[string][]string) + var resourceTypes []string + + for _, series := range data.TimeSeries { + if !containsLabel(resourceTypes, series.Resource.Type) { + resourceTypes = append(resourceTypes, series.Resource.Type) + } + } for _, series := range data.TimeSeries { points := make([]tsdb.TimePoint, 0) defaultMetricName := series.Metric.Type + if len(resourceTypes) > 1 { + defaultMetricName += " " + series.Resource.Type + } for key, value := range series.Metric.Labels { if !containsLabel(metricLabels[key], value) { @@ -385,7 +395,7 @@ func (e *StackdriverExecutor) parseResponse(queryRes *tsdb.QueryResult, data Sta points = append(points, tsdb.NewTimePoint(null.FloatFrom(value), float64((point.Interval.EndTime).Unix())*1000)) } - metricName := formatLegendKeys(series.Metric.Type, defaultMetricName, series.Metric.Labels, series.Resource.Labels, make(map[string]string), query) + metricName := formatLegendKeys(series.Metric.Type, defaultMetricName, series.Resource.Type, series.Metric.Labels, series.Resource.Labels, make(map[string]string), query) queryRes.Series = append(queryRes.Series, &tsdb.TimeSeries{ Name: metricName, @@ -411,7 +421,7 @@ func (e *StackdriverExecutor) parseResponse(queryRes *tsdb.QueryResult, data Sta bucketBound := calcBucketBound(point.Value.DistributionValue.BucketOptions, i) additionalLabels := map[string]string{"bucket": bucketBound} buckets[i] = &tsdb.TimeSeries{ - Name: formatLegendKeys(series.Metric.Type, defaultMetricName, series.Metric.Labels, series.Resource.Labels, additionalLabels, query), + Name: formatLegendKeys(series.Metric.Type, defaultMetricName, series.Resource.Type, series.Metric.Labels, series.Resource.Labels, additionalLabels, query), Points: make([]tsdb.TimePoint, 0), } if maxKey < i { @@ -427,7 +437,7 @@ func (e *StackdriverExecutor) parseResponse(queryRes *tsdb.QueryResult, data Sta bucketBound := calcBucketBound(point.Value.DistributionValue.BucketOptions, i) additionalLabels := map[string]string{"bucket": bucketBound} buckets[i] = &tsdb.TimeSeries{ - Name: formatLegendKeys(series.Metric.Type, defaultMetricName, series.Metric.Labels, series.Resource.Labels, additionalLabels, query), + Name: formatLegendKeys(series.Metric.Type, defaultMetricName, series.Resource.Type, series.Metric.Labels, series.Resource.Labels, additionalLabels, query), Points: make([]tsdb.TimePoint, 0), } } @@ -442,6 +452,7 @@ func (e *StackdriverExecutor) parseResponse(queryRes *tsdb.QueryResult, data Sta queryRes.Meta.Set("resourceLabels", resourceLabels) queryRes.Meta.Set("metricLabels", metricLabels) queryRes.Meta.Set("groupBys", query.GroupBys) + queryRes.Meta.Set("resourceTypes", resourceTypes) return nil } @@ -455,7 +466,7 @@ func containsLabel(labels []string, newLabel string) bool { return false } -func formatLegendKeys(metricType string, defaultMetricName string, metricLabels map[string]string, resourceLabels map[string]string, additionalLabels map[string]string, query *StackdriverQuery) string { +func formatLegendKeys(metricType string, defaultMetricName string, resourceType string, metricLabels map[string]string, resourceLabels map[string]string, additionalLabels map[string]string, query *StackdriverQuery) string { if query.AliasBy == "" { return defaultMetricName } @@ -469,6 +480,10 @@ func formatLegendKeys(metricType string, defaultMetricName string, metricLabels return []byte(metricType) } + if metaPartName == "resource.type" && resourceType != "" { + return []byte(resourceType) + } + metricPart := replaceWithMetricPart(metaPartName, metricType) if metricPart != nil { diff --git a/public/app/plugins/datasource/stackdriver/datasource.ts b/public/app/plugins/datasource/stackdriver/datasource.ts index b77abdbdab3..6955d15cba6 100644 --- a/public/app/plugins/datasource/stackdriver/datasource.ts +++ b/public/app/plugins/datasource/stackdriver/datasource.ts @@ -106,7 +106,6 @@ export default class StackdriverDatasource { if (!queryRes.series) { return; } - const unit = this.resolvePanelUnitFromTargets(options.targets); queryRes.series.forEach(series => { let timeSerie: any = { @@ -121,9 +120,10 @@ export default class StackdriverDatasource { result.push(timeSerie); }); }); + return { data: result }; + } else { + return { data: [] }; } - - return { data: result }; } async annotationQuery(options) { diff --git a/public/app/plugins/datasource/stackdriver/filter_segments.ts b/public/app/plugins/datasource/stackdriver/filter_segments.ts index 9eb27f31975..5adb56e2fcf 100644 --- a/public/app/plugins/datasource/stackdriver/filter_segments.ts +++ b/public/app/plugins/datasource/stackdriver/filter_segments.ts @@ -44,7 +44,7 @@ export class FilterSegments { this.removeSegment.value = DefaultRemoveFilterValue; return Promise.resolve([this.removeSegment]); } else { - return this.getFilterKeysFunc(); + return this.getFilterKeysFunc(segment, DefaultRemoveFilterValue); } } diff --git a/public/app/plugins/datasource/stackdriver/partials/query.filter.html b/public/app/plugins/datasource/stackdriver/partials/query.filter.html index 9ec59005a0b..5043161c492 100644 --- a/public/app/plugins/datasource/stackdriver/partials/query.filter.html +++ b/public/app/plugins/datasource/stackdriver/partials/query.filter.html @@ -28,7 +28,7 @@
Group By
- +
diff --git a/public/app/plugins/datasource/stackdriver/query_ctrl.ts b/public/app/plugins/datasource/stackdriver/query_ctrl.ts index 8e1f24edeb7..75c5035eede 100644 --- a/public/app/plugins/datasource/stackdriver/query_ctrl.ts +++ b/public/app/plugins/datasource/stackdriver/query_ctrl.ts @@ -101,6 +101,5 @@ export class StackdriverQueryCtrl extends QueryCtrl { this.lastQueryError = jsonBody.error.message; } } - console.error(err); } } diff --git a/public/app/plugins/datasource/stackdriver/query_filter_ctrl.ts b/public/app/plugins/datasource/stackdriver/query_filter_ctrl.ts index 786b2831e89..3ebff09f3de 100644 --- a/public/app/plugins/datasource/stackdriver/query_filter_ctrl.ts +++ b/public/app/plugins/datasource/stackdriver/query_filter_ctrl.ts @@ -1,6 +1,6 @@ import angular from 'angular'; import _ from 'lodash'; -import { FilterSegments, DefaultRemoveFilterValue } from './filter_segments'; +import { FilterSegments } from './filter_segments'; import appEvents from 'app/core/app_events'; export class StackdriverFilter { @@ -26,8 +26,10 @@ export class StackdriverFilter { export class StackdriverFilterCtrl { metricLabels: { [key: string]: string[] }; resourceLabels: { [key: string]: string[] }; + resourceTypes: string[]; defaultRemoveGroupByValue = '-- remove group by --'; + resourceTypeValue = 'resource.type'; loadLabelsPromise: Promise; service: string; @@ -72,7 +74,7 @@ export class StackdriverFilterCtrl { this.filterSegments = new FilterSegments( this.uiSegmentSrv, this.target, - this.getGroupBys.bind(this, null, null, DefaultRemoveFilterValue, false), + this.getFilterKeys.bind(this), this.getFilterValues.bind(this) ); this.filterSegments.buildSegmentModel(); @@ -141,6 +143,7 @@ export class StackdriverFilterCtrl { const data = await this.datasource.getLabels(this.target.metricType, this.target.refId); this.metricLabels = data.results[this.target.refId].meta.metricLabels; this.resourceLabels = data.results[this.target.refId].meta.resourceLabels; + this.resourceTypes = data.results[this.target.refId].meta.resourceTypes; resolve(); } catch (error) { if (error.data && error.data.message) { @@ -181,45 +184,66 @@ export class StackdriverFilterCtrl { this.$rootScope.$broadcast('metricTypeChanged'); } - async getGroupBys(segment, index, removeText?: string, removeUsed = true) { + async createLabelKeyElements() { await this.loadLabelsPromise; - const metricLabels = Object.keys(this.metricLabels || {}) - .filter(ml => { - if (!removeUsed) { - return true; - } - return this.target.aggregation.groupBys.indexOf('metric.label.' + ml) === -1; - }) - .map(l => { - return this.uiSegmentSrv.newSegment({ - value: `metric.label.${l}`, - expandable: false, - }); + let elements = Object.keys(this.metricLabels || {}).map(l => { + return this.uiSegmentSrv.newSegment({ + value: `metric.label.${l}`, + expandable: false, }); + }); - const resourceLabels = Object.keys(this.resourceLabels || {}) - .filter(ml => { - if (!removeUsed) { - return true; - } - - return this.target.aggregation.groupBys.indexOf('resource.label.' + ml) === -1; - }) - .map(l => { + elements = [ + ...elements, + ...Object.keys(this.resourceLabels || {}).map(l => { return this.uiSegmentSrv.newSegment({ value: `resource.label.${l}`, expandable: false, }); - }); + }), + ]; - const noValueOrPlusButton = !segment || segment.type === 'plus-button'; - if (noValueOrPlusButton && metricLabels.length === 0 && resourceLabels.length === 0) { - return Promise.resolve([]); + if (this.resourceTypes && this.resourceTypes.length > 0) { + elements = [ + ...elements, + this.uiSegmentSrv.newSegment({ + value: this.resourceTypeValue, + expandable: false, + }), + ]; } - this.removeSegment.value = removeText || this.defaultRemoveGroupByValue; - return Promise.resolve([...metricLabels, ...resourceLabels, this.removeSegment]); + return elements; + } + + async getFilterKeys(segment, removeText?: string) { + let elements = await this.createLabelKeyElements(); + + if (this.target.filters.indexOf(this.resourceTypeValue) !== -1) { + elements = elements.filter(e => e.value !== this.resourceTypeValue); + } + + const noValueOrPlusButton = !segment || segment.type === 'plus-button'; + if (noValueOrPlusButton && elements.length === 0) { + return []; + } + + this.removeSegment.value = removeText; + return [...elements, this.removeSegment]; + } + + async getGroupBys(segment) { + let elements = await this.createLabelKeyElements(); + + elements = elements.filter(e => this.target.aggregation.groupBys.indexOf(e.value) === -1); + const noValueOrPlusButton = !segment || segment.type === 'plus-button'; + if (noValueOrPlusButton && elements.length === 0) { + return []; + } + + this.removeSegment.value = this.defaultRemoveGroupByValue; + return [...elements, this.removeSegment]; } groupByChanged(segment, index) { @@ -263,6 +287,10 @@ export class StackdriverFilterCtrl { return this.resourceLabels[shortKey]; } + if (filterKey === this.resourceTypeValue) { + return this.resourceTypes; + } + return []; }