Ver Fonte

Merge pull request #3661 from mtanda/prometheus_fill_null

(prometheus) fill null for missing data point
Torkel Ödegaard há 10 anos atrás
pai
commit
f70252f013

+ 11 - 6
public/app/plugins/datasource/prometheus/datasource.js

@@ -91,7 +91,7 @@ function (angular, _, moment, dateMath) {
           delete self.lastErrors.query;
           delete self.lastErrors.query;
 
 
           _.each(response.data.data.result, function(metricData) {
           _.each(response.data.data.result, function(metricData) {
-            result.push(transformMetricData(metricData, options.targets[index]));
+            result.push(transformMetricData(metricData, options.targets[index], start, end));
           });
           });
         });
         });
 
 
@@ -207,14 +207,14 @@ function (angular, _, moment, dateMath) {
       return Math.ceil(sec * intervalFactor);
       return Math.ceil(sec * intervalFactor);
     };
     };
 
 
-    function transformMetricData(md, options) {
+    function transformMetricData(md, options, start, end) {
       var dps = [],
       var dps = [],
           metricLabel = null;
           metricLabel = null;
 
 
       metricLabel = createMetricLabel(md.metric, options);
       metricLabel = createMetricLabel(md.metric, options);
 
 
       var stepMs = parseInt(options.step) * 1000;
       var stepMs = parseInt(options.step) * 1000;
-      var lastTimestamp = null;
+      var baseTimestamp = start * 1000;
       _.each(md.values, function(value) {
       _.each(md.values, function(value) {
         var dp_value = parseFloat(value[1]);
         var dp_value = parseFloat(value[1]);
         if (_.isNaN(dp_value)) {
         if (_.isNaN(dp_value)) {
@@ -222,13 +222,18 @@ function (angular, _, moment, dateMath) {
         }
         }
 
 
         var timestamp = value[0] * 1000;
         var timestamp = value[0] * 1000;
-        if (lastTimestamp && (timestamp - lastTimestamp) > stepMs) {
-          dps.push([null, lastTimestamp + stepMs]);
+        for (var t = baseTimestamp; t < timestamp; t += stepMs) {
+          dps.push([null, t]);
         }
         }
-        lastTimestamp = timestamp;
+        baseTimestamp = timestamp + stepMs;
         dps.push([dp_value, timestamp]);
         dps.push([dp_value, timestamp]);
       });
       });
 
 
+      var endTimestamp = end * 1000;
+      for (var t = baseTimestamp; t <= endTimestamp; t += stepMs) {
+        dps.push([null, t]);
+      }
+
       return { target: metricLabel, datapoints: dps };
       return { target: metricLabel, datapoints: dps };
     }
     }
 
 

+ 67 - 0
public/app/plugins/datasource/prometheus/specs/datasource_specs.ts

@@ -49,6 +49,73 @@ describe('PrometheusDatasource', function() {
       expect(results.data[0].target).to.be('test{job="testjob"}');
       expect(results.data[0].target).to.be('test{job="testjob"}');
     });
     });
   });
   });
+  describe('When querying prometheus with one target which return multiple series', function() {
+    var results;
+    var start = 1443438675;
+    var end = 1443460275;
+    var step = 60;
+    var urlExpected = 'proxied/api/v1/query_range?query=' +
+                      encodeURIComponent('test{job="testjob"}') +
+                      '&start=' + start + '&end=' + end + '&step=' + step;
+    var query = {
+      range: { from: moment(1443438674760), to: moment(1443460274760) },
+      targets: [{ expr: 'test{job="testjob"}' }],
+      interval: '60s'
+    };
+    var response = {
+      status: "success",
+      data: {
+        resultType: "matrix",
+        result: [
+          {
+            metric: {"__name__": "test", job: "testjob", series: 'series 1'},
+            values: [
+              [start + step * 1, "3846"],
+              [start + step * 3, "3847"],
+              [end - step * 1, "3848"],
+            ]
+          },
+          {
+            metric: {"__name__": "test", job: "testjob", series: 'series 2'},
+            values: [
+              [start + step * 2, "4846"]
+            ]
+          },
+        ]
+      }
+    };
+    beforeEach(function() {
+      ctx.$httpBackend.expect('GET', urlExpected).respond(response);
+      ctx.ds.query(query).then(function(data) { results = data; });
+      ctx.$httpBackend.flush();
+    });
+    it('should be same length', function() {
+      expect(results.data.length).to.be(2);
+      expect(results.data[0].datapoints.length).to.be((end - start) / step + 1);
+      expect(results.data[1].datapoints.length).to.be((end - start) / step + 1);
+    });
+    it('should fill null until first datapoint in response', function() {
+      expect(results.data[0].datapoints[0][1]).to.be(start * 1000);
+      expect(results.data[0].datapoints[0][0]).to.be(null);
+      expect(results.data[0].datapoints[1][1]).to.be((start + step * 1) * 1000);
+      expect(results.data[0].datapoints[1][0]).to.be(3846);
+    });
+    it('should fill null after last datapoint in response', function() {
+      var length = (end - start) / step + 1;
+      expect(results.data[0].datapoints[length-2][1]).to.be((end - step * 1) * 1000);
+      expect(results.data[0].datapoints[length-2][0]).to.be(3848);
+      expect(results.data[0].datapoints[length-1][1]).to.be(end * 1000);
+      expect(results.data[0].datapoints[length-1][0]).to.be(null);
+    });
+    it('should fill null at gap between series', function() {
+      expect(results.data[0].datapoints[2][1]).to.be((start + step * 2) * 1000);
+      expect(results.data[0].datapoints[2][0]).to.be(null);
+      expect(results.data[1].datapoints[1][1]).to.be((start + step * 1) * 1000);
+      expect(results.data[1].datapoints[1][0]).to.be(null);
+      expect(results.data[1].datapoints[3][1]).to.be((start + step * 3) * 1000);
+      expect(results.data[1].datapoints[3][0]).to.be(null);
+    });
+  });
   describe('When performing metricFindQuery', function() {
   describe('When performing metricFindQuery', function() {
     var results;
     var results;
     var response;
     var response;