Merge branch 'master' into external-plugins
This commit is contained in:
@@ -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).
|
||||
|
||||
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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%">
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 };
|
||||
|
||||
@@ -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"}' }],
|
||||
|
||||
@@ -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() {
|
||||
|
||||
Reference in New Issue
Block a user