From 19d5d32229d874a98002785148a37c183dfbbe1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Wed, 5 Feb 2014 13:03:56 +0100 Subject: [PATCH] simplified a lot of the time series data manipulation prior to handing it to flot, kibana used a lot of code to sort and make sure all time points needed had values, this is not needed for graphite time series data. Fixes som strange issues with "null point mode" and inconsistent rendering between graphite and grafana flot, --- src/app/directives/grafanaGraph.js | 19 +-- src/app/panels/graphite/axisEditor.html | 4 - src/app/panels/graphite/module.js | 60 ++----- src/app/panels/graphite/styleEditor.html | 4 + src/app/panels/graphite/timeSeries.js | 194 +++-------------------- 5 files changed, 37 insertions(+), 244 deletions(-) diff --git a/src/app/directives/grafanaGraph.js b/src/app/directives/grafanaGraph.js index cd842c6c2b2..65ff8cba28a 100644 --- a/src/app/directives/grafanaGraph.js +++ b/src/app/directives/grafanaGraph.js @@ -82,7 +82,7 @@ function (angular, $, kbn, moment, _) { }); // Set barwidth based on specified interval - var barwidth = kbn.interval_to_ms(scope.panel.interval); + var barwidth = kbn.interval_to_ms(scope.interval); var stack = scope.panel.stack ? true : null; @@ -121,7 +121,7 @@ function (angular, $, kbn, moment, _) { mode: "time", min: _.isUndefined(scope.range.from) ? null : scope.range.from.getTime(), max: _.isUndefined(scope.range.to) ? null : scope.range.to.getTime(), - timeformat: time_format(scope.panel.interval), + timeformat: time_format(scope.interval), label: "Datetime", ticks: elem.width()/100 }, @@ -139,21 +139,8 @@ function (angular, $, kbn, moment, _) { addAnnotations(options); - // when rendering stacked bars, we need to ensure each point that has data is zero-filled - // so that the stacking happens in the proper order - var required_times = []; - if (data.length > 1) { - required_times = Array.prototype.concat.apply([], _.map(data, function (query) { - return query.time_series.getOrderedTimes(); - })); - required_times = _.uniq(required_times.sort(function (a, b) { - // decending numeric sort - return a-b; - }), true); - } - for (var i = 0; i < data.length; i++) { - var _d = data[i].time_series.getFlotPairs(required_times, scope.panel.nullPointMode); + var _d = data[i].time_series.getFlotPairs(scope.panel.nullPointMode); data[i].yaxis = data[i].info.yaxis; data[i].data = _d; data[i].info.y_format = data[i].yaxis === 1 ? scope.panel.y_format : scope.panel.y2_format; diff --git a/src/app/panels/graphite/axisEditor.html b/src/app/panels/graphite/axisEditor.html index 06e6418352d..3bf31cf4fb4 100644 --- a/src/app/panels/graphite/axisEditor.html +++ b/src/app/panels/graphite/axisEditor.html @@ -8,10 +8,6 @@
-
- - -
diff --git a/src/app/panels/graphite/module.js b/src/app/panels/graphite/module.js index 5a7f454a0d4..8298e57d04c 100644 --- a/src/app/panels/graphite/module.js +++ b/src/app/panels/graphite/module.js @@ -101,8 +101,8 @@ function (angular, app, $, _, kbn, moment, timeSeries) { /** @scratch /panels/histogram/3 * y_format:: 'none','bytes','short ' */ - y_format : 'none', - y2_format : 'none', + y_format : 'short', + y2_format : 'short', /** @scratch /panels/histogram/5 * grid object:: Min and max y-axis values * grid.min::: Minimum y-axis value @@ -129,23 +129,11 @@ function (angular, app, $, _, kbn, moment, timeSeries) { field : '_type', sort : ['_score','desc'] }, - /** @scratch /panels/histogram/3 - * ==== Interval options - * auto_int:: Automatically scale intervals? - */ - auto_int : true, /** @scratch /panels/histogram/3 * resolution:: If auto_int is true, shoot for this many bars. */ resolution : 100, - /** @scratch /panels/histogram/3 - * interval:: If auto_int is set to false, use this as the interval. - */ - interval : '5m', - /** @scratch /panels/histogram/3 - * interval:: Array of possible intervals in the *View* selector. Example [`auto',`1s',`5m',`3h'] - */ - intervals : ['auto','1s','1m','5m','10m','30m','1h','3h','12h','1d','1w','1y'], + /** @scratch /panels/histogram/3 * ==== Drawing options * lines:: Show line chart @@ -227,15 +215,6 @@ function (angular, app, $, _, kbn, moment, timeSeries) { }; - $scope.set_interval = function(interval) { - if(interval !== 'auto') { - $scope.panel.auto_int = false; - $scope.panel.interval = interval; - } else { - $scope.panel.auto_int = true; - } - }; - $scope.remove_panel_from_row = function(row, panel) { if ($scope.fullscreen) { $rootScope.$emit('panel-fullscreen-exit'); @@ -250,24 +229,15 @@ function (angular, app, $, _, kbn, moment, timeSeries) { $scope.get_data(); }; - $scope.interval_label = function(interval) { - return $scope.panel.auto_int && interval === $scope.panel.interval ? interval+" (auto)" : interval; - }; - $scope.updateTimeRange = function () { - var range = filterSrv.timeRange(); - var interval = filterSrv.timeRange(); + $scope.range = filterSrv.timeRange(); + $scope.interval = '10m'; - if ($scope.panel.auto_int) { - if (range) { - interval = kbn.secondsToHms( - kbn.calculate_interval(range.from, range.to, $scope.panel.resolution, 0) / 1000 - ); - } + if ($scope.range) { + $scope.interval = kbn.secondsToHms( + kbn.calculate_interval($scope.range.from, $scope.range.to, $scope.panel.resolution, 0) / 1000 + ); } - - $scope.interval = $scope.panel.interval = interval || '10m'; - $scope.range = range; }; $scope.colors = [ @@ -327,18 +297,10 @@ function (angular, app, $, _, kbn, moment, timeSeries) { var color = $scope.panel.aliasColors[alias] || $scope.colors[data.length]; var yaxis = $scope.panel.aliasYAxis[alias] || 1; - var tsOpts = { - interval: $scope.interval, + var time_series = new timeSeries.ZeroFilled({ start_date: $scope.range && $scope.range.from, end_date: $scope.range && $scope.range.to, - }; - - var time_series = new timeSeries.ZeroFilled(tsOpts); - - _.each(targetData.datapoints, function(valueArray) { - if (valueArray[0] !== null) { - time_series.addValue(valueArray[1] * 1000, valueArray[0]); - } + datapoints: targetData.datapoints }); var seriesInfo = { diff --git a/src/app/panels/graphite/styleEditor.html b/src/app/panels/graphite/styleEditor.html index e6d1f63b972..ec18121cba7 100644 --- a/src/app/panels/graphite/styleEditor.html +++ b/src/app/panels/graphite/styleEditor.html @@ -24,6 +24,10 @@
+
+ + +
diff --git a/src/app/panels/graphite/timeSeries.js b/src/app/panels/graphite/timeSeries.js index 0b325c5f13c..93c9eb87842 100644 --- a/src/app/panels/graphite/timeSeries.js +++ b/src/app/panels/graphite/timeSeries.js @@ -33,197 +33,41 @@ function (_, Interval) { */ ts.ZeroFilled = function (opts) { opts = _.defaults(opts, { - interval: '10m', start_date: null, end_date: null, + datapoints: [] }); - // the expected differenece between readings. - this.interval = new Interval(opts.interval); - - // will keep all values here, keyed by their time - this._data = {}; this.start_time = opts.start_date && getDatesTime(opts.start_date); this.end_time = opts.end_date && getDatesTime(opts.end_date); this.opts = opts; + this.datapoints = opts.datapoints; }; - /** - * Add a row - * @param {int} time The time for the value, in - * @param {any} value The value at this time - */ - ts.ZeroFilled.prototype.addValue = function (time, value) { - if (time instanceof Date) { - time = getDatesTime(time); - } else { - time = base10Int(time); - } - if (!isNaN(time)) { - this._data[time] = (_.isUndefined(value) ? 0 : value); - } - this._cached_times = null; - }; + ts.ZeroFilled.prototype.getFlotPairs = function (fillStyle) { + var result = []; - /** - * Get an array of the times that have been explicitly set in the series - * @param {array} include (optional) list of timestamps to include in the response - * @return {array} An array of integer times. - */ - ts.ZeroFilled.prototype.getOrderedTimes = function (include) { - var times = _.map(_.keys(this._data), base10Int); - if (_.isArray(include)) { - times = times.concat(include); - } - return _.uniq(times.sort(function (a, b) { - // decending numeric sort - return a - b; - }), true); - }; - - /** - * return the rows in the format: - * [ [time, value], [time, value], ... ] - * - * Heavy lifting is done by _get(Min|Default|All)FlotPairs() - * @param {array} required_times An array of timestamps that must be in the resulting pairs - * @return {array} - */ - ts.ZeroFilled.prototype.getFlotPairs = function (required_times, fillStyle) { - var times = this.getOrderedTimes(required_times), - strategy, - pairs; - - if(fillStyle === 'null as zero') { - strategy = this._getAllFlotPairs; - } else if(fillStyle === 'null') { - strategy = this._getNullFlotPairs; - } else if(fillStyle === 'connected') { - strategy = this._getNoZeroFlotPairs; - } else { - strategy = this._getMinFlotPairs; - } - - pairs = _.reduce( - times, // what - strategy, // how - [], // where - this // context - ); - - // if the first or last pair is inside either the start or end time, - // add those times to the series with null values so the graph will stretch to contain them. - // Removing, flot 0.8.1's max/min params satisfy this - /* - if (this.start_time && (pairs.length === 0 || pairs[0][0] > this.start_time)) { - pairs.unshift([this.start_time, null]); - } - if (this.end_time && (pairs.length === 0 || pairs[pairs.length - 1][0] < this.end_time)) { - pairs.push([this.end_time, null]); - } - */ - - return pairs; - }; - - /** - * ** called as a reduce stragegy in getFlotPairs() ** - * Fill zero's on either side of the current time, unless there is already a measurement there or - * we are looking at an edge. - * @return {array} An array of points to plot with flot - */ - ts.ZeroFilled.prototype._getMinFlotPairs = function (result, time, i, times) { - var next, expected_next, prev, expected_prev; - - // check for previous measurement - if (i > 0) { - prev = times[i - 1]; - expected_prev = this.interval.before(time); - if (prev < expected_prev) { - result.push([expected_prev, 0]); + _.each(this.datapoints, function(valueArray) { + var currentTime = valueArray[1]; + var currentValue = valueArray[0]; + if (currentValue === null) { + if (fillStyle === 'connected') { + return; + } + if (fillStyle === 'null as zero') { + currentValue = 0; + } + if (fillStyle === 'null') { + // do nothing + } } - } - // add the current time - result.push([ time, this._data[time] || 0]); - - // check for next measurement - if (times.length > i) { - next = times[i + 1]; - expected_next = this.interval.after(time); - if (next > expected_next) { - result.push([expected_next, 0]); - } - } + result.push([currentTime * 1000, currentValue]); + }); return result; }; - /** - * ** called as a reduce stragegy in getFlotPairs() ** - * Fill zero's to the right of each time, until the next measurement is reached or we are at the - * last measurement - * @return {array} An array of points to plot with flot - */ - ts.ZeroFilled.prototype._getAllFlotPairs = function (result, time, i, times) { - var next, expected_next; - - result.push([ times[i], this._data[times[i]] || 0 ]); - next = times[i + 1]; - expected_next = this.interval.after(time); - for(; times.length > i && next > expected_next; expected_next = this.interval.after(expected_next)) { - result.push([expected_next, 0]); - } - - return result; - }; - - /** - * ** called as a reduce stragegy in getFlotPairs() ** - * Same as min, but fills with nulls - * @return {array} An array of points to plot with flot - */ - ts.ZeroFilled.prototype._getNullFlotPairs = function (result, time, i, times) { - var next, expected_next, prev, expected_prev; - - // check for previous measurement - if (i > 0) { - prev = times[i - 1]; - expected_prev = this.interval.before(time); - if (prev < expected_prev) { - result.push([expected_prev, null]); - } - } - - // add the current time - result.push([ time, this._data[time] || null]); - - // check for next measurement - if (times.length > i) { - next = times[i + 1]; - expected_next = this.interval.after(time); - if (next > expected_next) { - result.push([expected_next, null]); - } - } - - return result; - }; - - /** - * ** called as a reduce stragegy in getFlotPairs() ** - * Not fill zero's on either side of the current time, only the current time - * @return {array} An array of points to plot with flot - */ - ts.ZeroFilled.prototype._getNoZeroFlotPairs = function (result, time) { - - // add the current time - if(this._data[time] !== null){ - result.push([ time, this._data[time]]); - } - - return result; - }; return ts; }); \ No newline at end of file