Browse Source

Merge branch 'master' into external-plugins

Torkel Ödegaard 10 years ago
parent
commit
63b50ab9b1

+ 2 - 2
docs/sources/installation/performance.md

@@ -8,7 +8,7 @@ page_keywords: grafana, performance, documentation
 
 ## Graphite
 
-Graphite 0.9.13 adds a much needed feature to the JSON rendering API
+Graphite 0.9.14 adds a much needed feature to the JSON rendering API
 that is very important for Grafana. If you are experiencing slow load &
 rendering times for large time ranges then it is most likely caused by
 running Graphite 0.9.12 or lower.
@@ -17,6 +17,6 @@ The latest version of Graphite adds a `maxDataPoints` parameter to the
 JSON render API. Without this feature Graphite can return hundreds of
 thousands of data points per graph, which can hang your browser. Be sure
 to upgrade to
-[0.9.13](http://graphite.readthedocs.org/en/latest/releases/0_9_13.html).
+[0.9.14](http://graphite.readthedocs.org/en/latest/releases/0_9_14.html).
 
 

+ 3 - 1
public/app/core/time_series.ts

@@ -101,6 +101,7 @@ class TimeSeries {
     var nullAsZero = fillStyle === 'null as zero';
     var currentTime;
     var currentValue;
+    var nonNulls = 0;
 
     for (var i = 0; i < this.datapoints.length; i++) {
       currentValue = this.datapoints[i][0];
@@ -117,6 +118,7 @@ class TimeSeries {
         if (_.isNumber(currentValue)) {
           this.stats.total += currentValue;
           this.allIsNull = false;
+          nonNulls++;
         }
 
         if (currentValue > this.stats.max) {
@@ -139,7 +141,7 @@ class TimeSeries {
     if (this.stats.min === Number.MAX_VALUE) { this.stats.min = null; }
 
     if (result.length) {
-      this.stats.avg = (this.stats.total / result.length);
+      this.stats.avg = (this.stats.total / nonNulls);
       this.stats.current = result[result.length-1][1];
       if (this.stats.current === null && result.length > 1) {
         this.stats.current = result[result.length-2][1];

+ 7 - 0
public/app/features/templating/editorCtrl.js

@@ -96,6 +96,13 @@ function (angular, _) {
       }
     };
 
+    $scope.duplicate = function(variable) {
+      $scope.current = angular.copy(variable);
+      $scope.variables.push($scope.current);
+      $scope.current.name = 'copy_of_'+variable.name;
+      $scope.updateSubmenuVisibility();
+    };
+
     $scope.update = function() {
       if ($scope.isValid()) {
         $scope.runQuery().then(function() {

+ 6 - 1
public/app/features/templating/partials/editor.html

@@ -39,7 +39,7 @@
 		<div ng-if="mode === 'list'">
 
 			<div class="editor-row row">
-				<div class="span8">
+				<div style="max-width: 1024px">
 					<div ng-if="variables.length === 0">
 						<em>No template variables defined</em>
 					</div>
@@ -59,6 +59,11 @@
 									Edit
 								</a>
 							</td>
+              <td style="width: 1%">
+                <a ng-click="duplicate(variable)" class="btn btn-inverse btn-small">
+                  Duplicate
+                </a>
+              </td>
 							<td style="width: 1%"><i ng-click="_.move(variables,$index,$index-1)" ng-hide="$first" class="pointer fa fa-arrow-up"></i></td>
 							<td style="width: 1%"><i ng-click="_.move(variables,$index,$index+1)" ng-hide="$last" class="pointer fa fa-arrow-down"></i></td>
 							<td style="width: 1%">

+ 1 - 1
public/app/plugins/datasource/cloudwatch/datasource.js

@@ -263,7 +263,7 @@ function (angular, _) {
         })
         .each(function(dp) {
           var timestamp = new Date(dp.Timestamp).getTime();
-          if (lastTimestamp && (timestamp - lastTimestamp) > periodMs * 2) {
+          if (lastTimestamp && (timestamp - lastTimestamp) > periodMs) {
             dps.push([null, lastTimestamp + periodMs]);
           }
           lastTimestamp = timestamp;

+ 1 - 1
public/app/plugins/datasource/cloudwatch/specs/datasource_specs.ts

@@ -55,7 +55,7 @@ describe('CloudWatchDatasource', function() {
         },
         {
           Average: 5,
-          Timestamp: 'Wed Dec 31 1969 16:20:00 GMT-0800 (PST)'
+          Timestamp: 'Wed Dec 31 1969 16:15:00 GMT-0800 (PST)'
         }
       ],
       Label: 'CPUUtilization'

+ 2 - 1
public/app/plugins/datasource/elasticsearch/query_builder.js

@@ -10,7 +10,7 @@ function (angular) {
 
   ElasticQueryBuilder.prototype.getRangeFilter = function() {
     var filter = {};
-    filter[this.timeField] = {"gte": "$timeFrom", "lte": "$timeTo"};
+    filter[this.timeField] = {"gte": "$timeFrom", "lte": "$timeTo", "format": "epoch_millis"};
     return filter;
   };
 
@@ -127,6 +127,7 @@ function (angular) {
             "interval": this.getInterval(aggDef),
             "field": this.timeField,
             "min_doc_count": 0,
+            "format": "epoch_millis",
             "extended_bounds": { "min": "$timeFrom", "max": "$timeTo" }
           };
           break;

+ 24 - 15
public/app/plugins/datasource/prometheus/datasource.js

@@ -48,6 +48,7 @@ function (angular, _, moment, dateMath) {
       var end = getPrometheusTime(options.range.to, true);
 
       var queries = [];
+      options = _.clone(options);
       _.each(options.targets, _.bind(function(target) {
         if (!target.expr || target.hide) {
           return;
@@ -58,7 +59,13 @@ function (angular, _, moment, dateMath) {
 
         var interval = target.interval || options.interval;
         var intervalFactor = target.intervalFactor || 1;
-        query.step = this.calculateInterval(interval, intervalFactor);
+        target.step = query.step = this.calculateInterval(interval, intervalFactor);
+        var range = Math.ceil(end - start);
+        // Prometheus drop query if range/step > 11000
+        // calibrate step if it is too big
+        if (query.step !== 0 && range / query.step > 11000) {
+          target.step = query.step = Math.ceil(range / 11000);
+        }
 
         queries.push(query);
       }, this));
@@ -96,17 +103,7 @@ function (angular, _, moment, dateMath) {
     };
 
     PrometheusDatasource.prototype.performTimeSeriesQuery = function(query, start, end) {
-      var url = '/api/v1/query_range?query=' + encodeURIComponent(query.expr) + '&start=' + start + '&end=' + end;
-
-      var step = query.step;
-      var range = Math.ceil(end - start);
-      // Prometheus drop query if range/step > 11000
-      // calibrate step if it is too big
-      if (step !== 0 && range / step > 11000) {
-        step = Math.ceil(range / 11000);
-      }
-      url += '&step=' + step;
-
+      var url = '/api/v1/query_range?query=' + encodeURIComponent(query.expr) + '&start=' + start + '&end=' + end + '&step=' + query.step;
       return this._request('GET', url);
     };
 
@@ -212,7 +209,7 @@ function (angular, _, moment, dateMath) {
         sec = 1;
       }
 
-      return Math.ceil(sec * intervalFactor) + 's';
+      return Math.ceil(sec * intervalFactor);
     };
 
     function transformMetricData(md, options) {
@@ -221,8 +218,20 @@ function (angular, _, moment, dateMath) {
 
       metricLabel = createMetricLabel(md.metric, options);
 
-      dps = _.map(md.values, function(value) {
-        return [parseFloat(value[1]), value[0] * 1000];
+      var stepMs = parseInt(options.step) * 1000;
+      var lastTimestamp = null;
+      _.each(md.values, function(value) {
+        var dp_value = parseFloat(value[1]);
+        if (_.isNaN(dp_value)) {
+          dp_value = null;
+        }
+
+        var timestamp = value[0] * 1000;
+        if (lastTimestamp && (timestamp - lastTimestamp) > stepMs) {
+          dps.push([null, lastTimestamp + stepMs]);
+        }
+        lastTimestamp = timestamp;
+        dps.push([dp_value, timestamp]);
       });
 
       return { target: metricLabel, datapoints: dps };

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

@@ -19,7 +19,7 @@ describe('PrometheusDatasource', function() {
     var results;
     var urlExpected = 'proxied/api/v1/query_range?query=' +
                       encodeURIComponent('test{job="testjob"}') +
-                      '&start=1443438675&end=1443460275&step=60s';
+                      '&start=1443438675&end=1443460275&step=60';
     var query = {
       range: { from: moment(1443438674760), to: moment(1443460274760) },
       targets: [{ expr: 'test{job="testjob"}' }],

+ 11 - 0
public/test/core/time_series_specs.js

@@ -43,6 +43,17 @@ define([
         expect(series.stats.max).to.be(-4);
       });
 
+      it('average value should ignore nulls', function() {
+        series = new TimeSeries(testData);
+        series.getFlotPairs('null', yAxisFormats);
+        expect(series.stats.avg).to.be(6.333333333333333);
+      });
+
+      it('with null as zero style, average value should treat nulls as 0', function() {
+        series = new TimeSeries(testData);
+        series.getFlotPairs('null as zero', yAxisFormats);
+        expect(series.stats.avg).to.be(4.75);
+      });
     });
 
     describe('series overrides', function() {