Browse Source

Merge pull request #9186 from mtanda/prometehus_instant_query2

(prometheus) instant query support
Carl Bergquist 8 years ago
parent
commit
da3aea5e0a

+ 26 - 2
public/app/plugins/datasource/prometheus/datasource.ts

@@ -99,6 +99,7 @@ export class PrometheusDatasource {
       var query: any = {};
       var query: any = {};
       query.expr = this.templateSrv.replace(target.expr, options.scopedVars, self.interpolateQueryExpr);
       query.expr = this.templateSrv.replace(target.expr, options.scopedVars, self.interpolateQueryExpr);
       query.requestId = options.panelId + target.refId;
       query.requestId = options.panelId + target.refId;
+      query.instant = target.instant;
 
 
       var interval = this.templateSrv.replace(target.interval, options.scopedVars) || options.interval;
       var interval = this.templateSrv.replace(target.interval, options.scopedVars) || options.interval;
       var intervalFactor = target.intervalFactor || 1;
       var intervalFactor = target.intervalFactor || 1;
@@ -114,7 +115,11 @@ export class PrometheusDatasource {
     }
     }
 
 
     var allQueryPromise = _.map(queries, query => {
     var allQueryPromise = _.map(queries, query => {
-      return this.performTimeSeriesQuery(query, start, end);
+      if (!query.instant) {
+        return this.performTimeSeriesQuery(query, start, end);
+      } else {
+        return this.performInstantQuery(query, end);
+      }
     });
     });
 
 
     return this.$q.all(allQueryPromise).then(responseList => {
     return this.$q.all(allQueryPromise).then(responseList => {
@@ -129,7 +134,11 @@ export class PrometheusDatasource {
           result.push(self.transformMetricDataToTable(response.data.data.result));
           result.push(self.transformMetricDataToTable(response.data.data.result));
         } else {
         } else {
           for (let metricData of response.data.data.result) {
           for (let metricData of response.data.data.result) {
-            result.push(self.transformMetricData(metricData, activeTargets[index], start, end));
+            if (response.data.data.resultType === 'matrix') {
+              result.push(self.transformMetricData(metricData, activeTargets[index], start, end));
+            } else if (response.data.data.resultType === 'vector') {
+              result.push(self.transformInstantMetricData(metricData, activeTargets[index]));
+            }
           }
           }
         }
         }
       });
       });
@@ -156,6 +165,11 @@ export class PrometheusDatasource {
     return this._request('GET', url, query.requestId);
     return this._request('GET', url, query.requestId);
   }
   }
 
 
+  performInstantQuery(query, time) {
+    var url = '/api/v1/query?query=' + encodeURIComponent(query.expr) + '&time=' + time;
+    return this._request('GET', url, query.requestId);
+  }
+
   performSuggestQuery(query, cache = false) {
   performSuggestQuery(query, cache = false) {
     var url = '/api/v1/label/__name__/values';
     var url = '/api/v1/label/__name__/values';
 
 
@@ -317,6 +331,9 @@ export class PrometheusDatasource {
 
 
     // Populate rows, set value to empty string when label not present.
     // Populate rows, set value to empty string when label not present.
     _.each(md, function(series) {
     _.each(md, function(series) {
+      if (series.value) {
+        series.values = [series.value];
+      }
       if (series.values) {
       if (series.values) {
         for (i = 0; i < series.values.length; i++) {
         for (i = 0; i < series.values.length; i++) {
           var values = series.values[i];
           var values = series.values[i];
@@ -340,6 +357,13 @@ export class PrometheusDatasource {
     return table;
     return table;
   }
   }
 
 
+  transformInstantMetricData(md, options) {
+    var dps = [], metricLabel = null;
+    metricLabel = this.createMetricLabel(md.metric, options);
+    dps.push([parseFloat(md.value[1]), md.value[0] * 1000]);
+    return { target: metricLabel, datapoints: dps };
+  }
+
   createMetricLabel(labelData, options) {
   createMetricLabel(labelData, options) {
     if (_.isUndefined(options) || _.isEmpty(options.legendFormat)) {
     if (_.isUndefined(options) || _.isEmpty(options.legendFormat)) {
       return this.getOriginalMetricName(labelData);
       return this.getOriginalMetricName(labelData);

+ 1 - 3
public/app/plugins/datasource/prometheus/metric_find_query.js

@@ -95,9 +95,7 @@ function (_) {
 
 
   PrometheusMetricFindQuery.prototype.queryResultQuery = function(query) {
   PrometheusMetricFindQuery.prototype.queryResultQuery = function(query) {
     var end = this.datasource.getPrometheusTime(this.range.to, true);
     var end = this.datasource.getPrometheusTime(this.range.to, true);
-    var url = '/api/v1/query?query=' + encodeURIComponent(query) + '&time=' + end;
-
-    return this.datasource._request('GET', url)
+    return this.datasource.performInstantQuery({ expr: query }, end)
     .then(function(result) {
     .then(function(result) {
       return _.map(result.data.data.result, function(metricData) {
       return _.map(result.data.data.result, function(metricData) {
         var text = metricData.metric.__name__ || '';
         var text = metricData.metric.__name__ || '';

+ 2 - 0
public/app/plugins/datasource/prometheus/partials/query.editor.html

@@ -45,6 +45,8 @@
 			<div class="gf-form-select-wrapper width-8">
 			<div class="gf-form-select-wrapper width-8">
 				<select class="gf-form-input gf-size-auto" ng-model="ctrl.target.format" ng-options="f.value as f.text for f in ctrl.formats" ng-change="ctrl.refresh()"></select>
 				<select class="gf-form-input gf-size-auto" ng-model="ctrl.target.format" ng-options="f.value as f.text for f in ctrl.formats" ng-change="ctrl.refresh()"></select>
 			</div>
 			</div>
+			<gf-form-switch class="gf-form" label="Instant" label-class="width-5" checked="ctrl.target.instant" on-change="ctrl.refresh()">
+			</gf-form-switch>
 			<label class="gf-form-label">
 			<label class="gf-form-label">
 				<a href="{{ctrl.linkToPrometheus}}" target="_blank" bs-tooltip="'Link to Graph in Prometheus'">
 				<a href="{{ctrl.linkToPrometheus}}" target="_blank" bs-tooltip="'Link to Graph in Prometheus'">
 					<i class="fa fa-share-square-o"></i>
 					<i class="fa fa-share-square-o"></i>

+ 3 - 0
public/app/plugins/datasource/prometheus/query_ctrl.ts

@@ -12,6 +12,7 @@ class PrometheusQueryCtrl extends QueryCtrl {
   metric: any;
   metric: any;
   resolutions: any;
   resolutions: any;
   formats: any;
   formats: any;
+  instant: any;
   oldTarget: any;
   oldTarget: any;
   suggestMetrics: any;
   suggestMetrics: any;
   getMetricsAutocomplete: any;
   getMetricsAutocomplete: any;
@@ -36,6 +37,8 @@ class PrometheusQueryCtrl extends QueryCtrl {
       {text: 'Table', value: 'table'},
       {text: 'Table', value: 'table'},
     ];
     ];
 
 
+    this.instant = false;
+
     this.updateLink();
     this.updateLink();
   }
   }
 
 

+ 77 - 3
public/app/plugins/datasource/prometheus/specs/datasource_specs.ts

@@ -26,7 +26,7 @@ describe('PrometheusDatasource', function() {
                       '&start=1443438675&end=1443460275&step=60';
                       '&start=1443438675&end=1443460275&step=60';
     var query = {
     var query = {
       range: { from: moment(1443438674760), to: moment(1443460274760) },
       range: { from: moment(1443438674760), to: moment(1443460274760) },
-      targets: [{ expr: 'test{job="testjob"}' }],
+      targets: [{ expr: 'test{job="testjob"}', format: 'time_series' }],
       interval: '60s'
       interval: '60s'
     };
     };
     var response = {
     var response = {
@@ -62,7 +62,7 @@ describe('PrometheusDatasource', function() {
                       '&start=' + start + '&end=' + end + '&step=' + step;
                       '&start=' + start + '&end=' + end + '&step=' + step;
     var query = {
     var query = {
       range: { from: moment(1443438674760), to: moment(1443460274760) },
       range: { from: moment(1443438674760), to: moment(1443460274760) },
-      targets: [{ expr: 'test{job="testjob"}' }],
+      targets: [{ expr: 'test{job="testjob"}', format: 'time_series' }],
       interval: '60s'
       interval: '60s'
     };
     };
     var response = {
     var response = {
@@ -119,7 +119,40 @@ describe('PrometheusDatasource', function() {
       expect(results.data[1].datapoints[3][0]).to.be(null);
       expect(results.data[1].datapoints[3][0]).to.be(null);
     });
     });
   });
   });
-  describe('When performing annotationQuery', function() {
+  describe('When querying prometheus with one target and instant = true', function () {
+    var results;
+    var urlExpected = 'proxied/api/v1/query?query=' +
+      encodeURIComponent('test{job="testjob"}') +
+      '&time=1443460275';
+    var query = {
+      range: { from: moment(1443438674760), to: moment(1443460274760) },
+      targets: [{ expr: 'test{job="testjob"}', format: 'time_series', instant: true }],
+      interval: '60s'
+    };
+    var response = {
+      status: "success",
+      data: {
+        resultType: "vector",
+        result: [{
+          metric: { "__name__": "test", job: "testjob" },
+          value: [1443454528, "3846"]
+        }]
+      }
+    };
+    beforeEach(function () {
+      ctx.$httpBackend.expect('GET', urlExpected).respond(response);
+      ctx.ds.query(query).then(function (data) { results = data; });
+      ctx.$httpBackend.flush();
+    });
+    it('should generate the correct query', function () {
+      ctx.$httpBackend.verifyNoOutstandingExpectation();
+    });
+    it('should return series list', function () {
+      expect(results.data.length).to.be(1);
+      expect(results.data[0].target).to.be('test{job="testjob"}');
+    });
+  });
+  describe('When performing annotationQuery', function () {
     var results;
     var results;
     var urlExpected = 'proxied/api/v1/query_range?query=' +
     var urlExpected = 'proxied/api/v1/query_range?query=' +
                       encodeURIComponent('ALERTS{alertstate="firing"}') +
                       encodeURIComponent('ALERTS{alertstate="firing"}') +
@@ -195,4 +228,45 @@ describe('PrometheusDatasource', function() {
       );
       );
     });
     });
   });
   });
+  describe('When resultFormat is table and instant = true', function() {
+    var results;
+    var urlExpected = 'proxied/api/v1/query?query=' +
+      encodeURIComponent('test{job="testjob"}') +
+      '&time=1443460275';
+    var query = {
+      range: { from: moment(1443438674760), to: moment(1443460274760) },
+      targets: [{ expr: 'test{job="testjob"}', format: 'time_series', instant: true }],
+      interval: '60s'
+    };
+    var response = {
+      status: "success",
+      data: {
+        resultType: "vector",
+        result: [{
+          metric: { "__name__": "test", job: "testjob" },
+          value: [1443454528, "3846"]
+        }]
+      }
+    };
+    beforeEach(function () {
+      ctx.$httpBackend.expect('GET', urlExpected).respond(response);
+      ctx.ds.query(query).then(function (data) { results = data; });
+      ctx.$httpBackend.flush();
+    });
+    it('should return table model', function() {
+      var table = ctx.ds.transformMetricDataToTable(response.data.result);
+      expect(table.type).to.be('table');
+      expect(table.rows).to.eql(
+        [
+          [ 1443454528000, 'test', 'testjob', 3846]
+        ]);
+      expect(table.columns).to.eql(
+        [ { text: 'Time', type: 'time' },
+          { text: '__name__' },
+          { text: 'job' },
+          { text: 'Value' }
+        ]
+      );
+    });
+  });
 });
 });