瀏覽代碼

stackdriver: wip - group bys

Daniel Lee 7 年之前
父節點
當前提交
9c8593e71f

+ 16 - 1
pkg/tsdb/stackdriver/stackdriver.go

@@ -133,6 +133,12 @@ func setAggParams(params *url.Values, query *tsdb.Query) {
 		params.Add("aggregation.alignmentPeriod", "+60s")
 	}
 
+	groupBys := query.Model.Get("groupBys").MustArray()
+	if len(groupBys) > 0 {
+		for i := 0; i < len(groupBys); i++ {
+			params.Add("aggregation.groupByFields", groupBys[i].(string))
+		}
+	}
 }
 
 func (e *StackdriverExecutor) executeQuery(ctx context.Context, query *StackdriverQuery, tsdbQuery *tsdb.TsdbQuery) (*tsdb.QueryResult, error) {
@@ -205,6 +211,9 @@ func (e *StackdriverExecutor) unmarshalResponse(res *http.Response) (StackDriver
 }
 
 func (e *StackdriverExecutor) parseResponse(queryRes *tsdb.QueryResult, data StackDriverResponse) error {
+	metricLabels := make(map[string][]string)
+	// resourceLabels := make(map[string][]string)
+
 	for _, series := range data.TimeSeries {
 		points := make([]tsdb.TimePoint, 0)
 
@@ -215,15 +224,21 @@ func (e *StackdriverExecutor) parseResponse(queryRes *tsdb.QueryResult, data Sta
 		}
 		metricName := series.Metric.Type
 
-		for _, value := range series.Metric.Labels {
+		for key, value := range series.Metric.Labels {
+			metricLabels[key] = append(metricLabels[key], value)
 			metricName += " " + value
 		}
+
+		// queryRes.Meta.Set("resourceLabels", series.Resource.Labels)
+
 		queryRes.Series = append(queryRes.Series, &tsdb.TimeSeries{
 			Name:   metricName,
 			Points: points,
 		})
 	}
 
+	queryRes.Meta.Set("metricLabels", metricLabels)
+
 	return nil
 }
 

+ 28 - 0
pkg/tsdb/stackdriver/stackdriver_test.go

@@ -71,6 +71,29 @@ func TestStackdriver(t *testing.T) {
 				So(queries[0].Params["filter"][0], ShouldEqual, "metric.type=\"a/metric/type\"")
 			})
 
+			Convey("and query has group bys", func() {
+				tsdbQuery.Queries[0].Model = simplejson.NewFromAny(map[string]interface{}{
+					"target":             "target",
+					"metricType":         "a/metric/type",
+					"primaryAggregation": "REDUCE_NONE",
+					"groupBys":           []interface{}{"metric.label.group1", "metric.label.group2"},
+				})
+
+				queries, err := executor.buildQueries(tsdbQuery)
+				So(err, ShouldBeNil)
+
+				So(len(queries), ShouldEqual, 1)
+				So(queries[0].RefID, ShouldEqual, "A")
+				So(queries[0].Target, ShouldEqual, "target")
+				So(len(queries[0].Params), ShouldEqual, 5)
+				So(queries[0].Params["interval.startTime"][0], ShouldEqual, "2018-03-15T13:00:00Z")
+				So(queries[0].Params["interval.endTime"][0], ShouldEqual, "2018-03-15T13:34:00Z")
+				So(queries[0].Params["aggregation.perSeriesAligner"][0], ShouldEqual, "ALIGN_NONE")
+				So(queries[0].Params["aggregation.groupByFields"][0], ShouldEqual, "metric.label.group1")
+				So(queries[0].Params["aggregation.groupByFields"][1], ShouldEqual, "metric.label.group2")
+				So(queries[0].Params["filter"][0], ShouldEqual, "metric.type=\"a/metric/type\"")
+			})
+
 		})
 
 		Convey("Parse stackdriver response in the time series format", func() {
@@ -127,6 +150,11 @@ func TestStackdriver(t *testing.T) {
 					So(res.Series[0].Points[1][0].Float64, ShouldEqual, 9.7323568146676)
 					So(res.Series[0].Points[2][0].Float64, ShouldEqual, 9.7730520330369)
 				})
+
+				Convey("Should add meta for labels to the response", func() {
+					instanceName := res.Meta.Get("metricLabels").MustMap()["instance_name"]
+					So(instanceName, ShouldNotBeNil)
+				})
 			})
 		})
 	})

+ 2 - 1
public/app/plugins/datasource/stackdriver/datasource.ts

@@ -16,7 +16,8 @@ export default class StackdriverDatasource {
       refId: t.refId,
       datasourceId: this.id,
       metricType: t.metricType,
-      primaryAggregation: t.aggregation,
+      primaryAggregation: t.aggregation.crossSeriesReducer,
+      groupBys: t.aggregation.groupBys,
     }));
 
     const result = [];

+ 8 - 3
public/app/plugins/datasource/stackdriver/partials/query.editor.html

@@ -10,13 +10,18 @@
     </div>
   </div>
   <div class="gf-form-inline">
-    <div class="gf-form gf-form--grow">
+    <div class="gf-form">
       <label class="gf-form-label query-keyword width-9">Aggregation</label>
       <div class="gf-form-select-wrapper gf-form-select-wrapper--caret-indent">
-        <select class="gf-form-input width-11" ng-model="ctrl.target.aggregation" ng-options="f.value as f.text for f in ctrl.aggOptions"
+        <select class="gf-form-input width-11" ng-model="ctrl.target.aggregation.crossSeriesReducer" ng-options="f.value as f.text for f in ctrl.aggOptions"
           ng-change="ctrl.refresh()"></select>
       </div>
-      <div class="gf-form-label gf-form-label--grow"></div>
+    </div>
+    <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>
+      </div>
     </div>
     <div class="gf-form gf-form--grow">
       <div class="gf-form-label gf-form-label--grow"></div>

+ 52 - 2
public/app/plugins/datasource/stackdriver/query_ctrl.ts

@@ -5,6 +5,7 @@ import appEvents from 'app/core/app_events';
 export interface QueryMeta {
   rawQuery: string;
   rawQueryString: string;
+  metricLabels: any;
 }
 export class StackdriverQueryCtrl extends QueryCtrl {
   static templateUrl = 'partials/query.editor.html';
@@ -15,6 +16,12 @@ export class StackdriverQueryCtrl extends QueryCtrl {
     };
     metricType: string;
     refId: string;
+    aggregation: {
+      crossSeriesReducer: string;
+      alignmentPeriod: string;
+      perSeriesAligner: string;
+      groupBys: string[];
+    };
   };
   defaultDropdownValue = 'Select metric';
 
@@ -24,9 +31,16 @@ export class StackdriverQueryCtrl extends QueryCtrl {
       name: 'loading project...',
     },
     metricType: this.defaultDropdownValue,
-    aggregation: 'REDUCE_MEAN',
+    aggregation: {
+      crossSeriesReducer: 'REDUCE_MEAN',
+      alignmentPeriod: '',
+      perSeriesAligner: '',
+      groupBys: [],
+    },
   };
 
+  groupBySegments: any[];
+
   aggOptions = [
     { text: 'none', value: 'REDUCE_NONE' },
     { text: 'mean', value: 'REDUCE_MEAN' },
@@ -47,7 +61,7 @@ export class StackdriverQueryCtrl extends QueryCtrl {
   lastQueryError?: string;
 
   /** @ngInject */
-  constructor($scope, $injector) {
+  constructor($scope, $injector, private uiSegmentSrv) {
     super($scope, $injector);
     _.defaultsDeep(this.target, this.defaults);
 
@@ -55,6 +69,11 @@ export class StackdriverQueryCtrl extends QueryCtrl {
     this.panelCtrl.events.on('data-error', this.onDataError.bind(this), $scope);
 
     this.getCurrentProject().then(this.getMetricTypes.bind(this));
+
+    this.groupBySegments = _.map(this.target.aggregation.groupBys, groupBy => {
+      return uiSegmentSrv.getSegmentForValue(groupBy);
+    });
+    this.ensurePlusButton(this.groupBySegments);
   }
 
   async getCurrentProject() {
@@ -97,6 +116,37 @@ export class StackdriverQueryCtrl extends QueryCtrl {
     }
   }
 
+  getGroupBys() {
+    const segments = _.map(Object.keys(this.lastQueryMeta.metricLabels), (label: string) => {
+      return this.uiSegmentSrv.newSegment({ value: label, expandable: false });
+    });
+
+    return Promise.resolve(segments);
+  }
+
+  groupByChanged(segment, index) {
+    this.target.aggregation.groupBys = _.reduce(
+      this.groupBySegments,
+      function(memo, seg) {
+        if (!seg.fake) {
+          memo.push(seg.value);
+        }
+        return memo;
+      },
+      []
+    );
+    this.ensurePlusButton(this.groupBySegments);
+  }
+
+  ensurePlusButton(segments) {
+    const count = segments.length;
+    const lastSegment = segments[Math.max(count - 1, 0)];
+
+    if (!lastSegment || lastSegment.type !== 'plus-button') {
+      segments.push(this.uiSegmentSrv.newPlusButton());
+    }
+  }
+
   onDataReceived(dataList) {
     this.lastQueryError = null;
     this.lastQueryMeta = null;