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 c5af0bf1c5)
This commit is contained in:
@@ -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.
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
<div class="gf-form">
|
||||
<span class="gf-form-label query-keyword width-9">Group By</span>
|
||||
<div class="gf-form" ng-repeat="segment in ctrl.groupBySegments">
|
||||
<metric-segment segment="segment" get-options="ctrl.getGroupBys(segment, $index)" on-change="ctrl.groupByChanged(segment, $index)"></metric-segment>
|
||||
<metric-segment segment="segment" get-options="ctrl.getGroupBys(segment)" on-change="ctrl.groupByChanged(segment, $index)"></metric-segment>
|
||||
</div>
|
||||
</div>
|
||||
<div class="gf-form gf-form--grow">
|
||||
|
||||
@@ -101,6 +101,5 @@ export class StackdriverQueryCtrl extends QueryCtrl {
|
||||
this.lastQueryError = jsonBody.error.message;
|
||||
}
|
||||
}
|
||||
console.error(err);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<any>;
|
||||
|
||||
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 [];
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user