Commit 63b50ab9 by Torkel Ödegaard

Merge branch 'master' into external-plugins

parents 69daede5 c0beef75
...@@ -8,7 +8,7 @@ page_keywords: grafana, performance, documentation ...@@ -8,7 +8,7 @@ page_keywords: grafana, performance, documentation
## Graphite ## 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 & 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 rendering times for large time ranges then it is most likely caused by
running Graphite 0.9.12 or lower. running Graphite 0.9.12 or lower.
...@@ -17,6 +17,6 @@ The latest version of Graphite adds a `maxDataPoints` parameter to the ...@@ -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 JSON render API. Without this feature Graphite can return hundreds of
thousands of data points per graph, which can hang your browser. Be sure thousands of data points per graph, which can hang your browser. Be sure
to upgrade to 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).
...@@ -101,6 +101,7 @@ class TimeSeries { ...@@ -101,6 +101,7 @@ class TimeSeries {
var nullAsZero = fillStyle === 'null as zero'; var nullAsZero = fillStyle === 'null as zero';
var currentTime; var currentTime;
var currentValue; var currentValue;
var nonNulls = 0;
for (var i = 0; i < this.datapoints.length; i++) { for (var i = 0; i < this.datapoints.length; i++) {
currentValue = this.datapoints[i][0]; currentValue = this.datapoints[i][0];
...@@ -117,6 +118,7 @@ class TimeSeries { ...@@ -117,6 +118,7 @@ class TimeSeries {
if (_.isNumber(currentValue)) { if (_.isNumber(currentValue)) {
this.stats.total += currentValue; this.stats.total += currentValue;
this.allIsNull = false; this.allIsNull = false;
nonNulls++;
} }
if (currentValue > this.stats.max) { if (currentValue > this.stats.max) {
...@@ -139,7 +141,7 @@ class TimeSeries { ...@@ -139,7 +141,7 @@ class TimeSeries {
if (this.stats.min === Number.MAX_VALUE) { this.stats.min = null; } if (this.stats.min === Number.MAX_VALUE) { this.stats.min = null; }
if (result.length) { 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]; this.stats.current = result[result.length-1][1];
if (this.stats.current === null && result.length > 1) { if (this.stats.current === null && result.length > 1) {
this.stats.current = result[result.length-2][1]; this.stats.current = result[result.length-2][1];
......
...@@ -96,6 +96,13 @@ function (angular, _) { ...@@ -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() { $scope.update = function() {
if ($scope.isValid()) { if ($scope.isValid()) {
$scope.runQuery().then(function() { $scope.runQuery().then(function() {
......
...@@ -39,7 +39,7 @@ ...@@ -39,7 +39,7 @@
<div ng-if="mode === 'list'"> <div ng-if="mode === 'list'">
<div class="editor-row row"> <div class="editor-row row">
<div class="span8"> <div style="max-width: 1024px">
<div ng-if="variables.length === 0"> <div ng-if="variables.length === 0">
<em>No template variables defined</em> <em>No template variables defined</em>
</div> </div>
...@@ -59,6 +59,11 @@ ...@@ -59,6 +59,11 @@
Edit Edit
</a> </a>
</td> </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="$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%"><i ng-click="_.move(variables,$index,$index+1)" ng-hide="$last" class="pointer fa fa-arrow-down"></i></td>
<td style="width: 1%"> <td style="width: 1%">
......
...@@ -263,7 +263,7 @@ function (angular, _) { ...@@ -263,7 +263,7 @@ function (angular, _) {
}) })
.each(function(dp) { .each(function(dp) {
var timestamp = new Date(dp.Timestamp).getTime(); var timestamp = new Date(dp.Timestamp).getTime();
if (lastTimestamp && (timestamp - lastTimestamp) > periodMs * 2) { if (lastTimestamp && (timestamp - lastTimestamp) > periodMs) {
dps.push([null, lastTimestamp + periodMs]); dps.push([null, lastTimestamp + periodMs]);
} }
lastTimestamp = timestamp; lastTimestamp = timestamp;
......
...@@ -55,7 +55,7 @@ describe('CloudWatchDatasource', function() { ...@@ -55,7 +55,7 @@ describe('CloudWatchDatasource', function() {
}, },
{ {
Average: 5, 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' Label: 'CPUUtilization'
......
...@@ -10,7 +10,7 @@ function (angular) { ...@@ -10,7 +10,7 @@ function (angular) {
ElasticQueryBuilder.prototype.getRangeFilter = function() { ElasticQueryBuilder.prototype.getRangeFilter = function() {
var filter = {}; var filter = {};
filter[this.timeField] = {"gte": "$timeFrom", "lte": "$timeTo"}; filter[this.timeField] = {"gte": "$timeFrom", "lte": "$timeTo", "format": "epoch_millis"};
return filter; return filter;
}; };
...@@ -127,6 +127,7 @@ function (angular) { ...@@ -127,6 +127,7 @@ function (angular) {
"interval": this.getInterval(aggDef), "interval": this.getInterval(aggDef),
"field": this.timeField, "field": this.timeField,
"min_doc_count": 0, "min_doc_count": 0,
"format": "epoch_millis",
"extended_bounds": { "min": "$timeFrom", "max": "$timeTo" } "extended_bounds": { "min": "$timeFrom", "max": "$timeTo" }
}; };
break; break;
......
...@@ -48,6 +48,7 @@ function (angular, _, moment, dateMath) { ...@@ -48,6 +48,7 @@ function (angular, _, moment, dateMath) {
var end = getPrometheusTime(options.range.to, true); var end = getPrometheusTime(options.range.to, true);
var queries = []; var queries = [];
options = _.clone(options);
_.each(options.targets, _.bind(function(target) { _.each(options.targets, _.bind(function(target) {
if (!target.expr || target.hide) { if (!target.expr || target.hide) {
return; return;
...@@ -58,7 +59,13 @@ function (angular, _, moment, dateMath) { ...@@ -58,7 +59,13 @@ function (angular, _, moment, dateMath) {
var interval = target.interval || options.interval; var interval = target.interval || options.interval;
var intervalFactor = target.intervalFactor || 1; 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); queries.push(query);
}, this)); }, this));
...@@ -96,17 +103,7 @@ function (angular, _, moment, dateMath) { ...@@ -96,17 +103,7 @@ function (angular, _, moment, dateMath) {
}; };
PrometheusDatasource.prototype.performTimeSeriesQuery = function(query, start, end) { PrometheusDatasource.prototype.performTimeSeriesQuery = function(query, start, end) {
var url = '/api/v1/query_range?query=' + encodeURIComponent(query.expr) + '&start=' + start + '&end=' + end; var url = '/api/v1/query_range?query=' + encodeURIComponent(query.expr) + '&start=' + start + '&end=' + end + '&step=' + query.step;
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;
return this._request('GET', url); return this._request('GET', url);
}; };
...@@ -212,7 +209,7 @@ function (angular, _, moment, dateMath) { ...@@ -212,7 +209,7 @@ function (angular, _, moment, dateMath) {
sec = 1; sec = 1;
} }
return Math.ceil(sec * intervalFactor) + 's'; return Math.ceil(sec * intervalFactor);
}; };
function transformMetricData(md, options) { function transformMetricData(md, options) {
...@@ -221,8 +218,20 @@ function (angular, _, moment, dateMath) { ...@@ -221,8 +218,20 @@ function (angular, _, moment, dateMath) {
metricLabel = createMetricLabel(md.metric, options); metricLabel = createMetricLabel(md.metric, options);
dps = _.map(md.values, function(value) { var stepMs = parseInt(options.step) * 1000;
return [parseFloat(value[1]), value[0] * 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 }; return { target: metricLabel, datapoints: dps };
......
...@@ -19,7 +19,7 @@ describe('PrometheusDatasource', function() { ...@@ -19,7 +19,7 @@ describe('PrometheusDatasource', function() {
var results; var results;
var urlExpected = 'proxied/api/v1/query_range?query=' + var urlExpected = 'proxied/api/v1/query_range?query=' +
encodeURIComponent('test{job="testjob"}') + encodeURIComponent('test{job="testjob"}') +
'&start=1443438675&end=1443460275&step=60s'; '&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"}' }],
......
...@@ -43,6 +43,17 @@ define([ ...@@ -43,6 +43,17 @@ define([
expect(series.stats.max).to.be(-4); 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() { describe('series overrides', function() {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment