From 2cdd097b3aeaf54b146bb1daf6ce6bb5d226eb14 Mon Sep 17 00:00:00 2001 From: benrubson Date: Sat, 15 Oct 2016 11:43:36 +0200 Subject: [PATCH 01/27] Missing values, correct timeStep --- public/app/plugins/panel/graph/graph.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/public/app/plugins/panel/graph/graph.ts b/public/app/plugins/panel/graph/graph.ts index 6a3e49b8455..40ecfd4dfb2 100755 --- a/public/app/plugins/panel/graph/graph.ts +++ b/public/app/plugins/panel/graph/graph.ts @@ -271,8 +271,14 @@ module.directive('grafanaGraph', function($rootScope, timeSrv) { break; } default: { - if (data.length && data[0].stats.timeStep) { - options.series.bars.barWidth = data[0].stats.timeStep / 1.5; + var width; + for (let i = 0; i < data.length; i++) { + if (data[i].stats.timeStep && (width == null || (data[i].stats.timeStep / 1.5) < width)) { + width = data[i].stats.timeStep / 1.5; + } + } + if (width) { + options.series.bars.barWidth = width; } addTimeAxis(options); break; From c976448f6a7d5c41178643ae9baa8ff9a0d051ca Mon Sep 17 00:00:00 2001 From: benrubson Date: Sat, 15 Oct 2016 13:07:41 +0200 Subject: [PATCH 02/27] Missing values, stack null & missing values --- .../app/plugins/panel/graph/graph_tooltip.js | 7 +- public/vendor/flot/jquery.flot.js | 29 +++--- public/vendor/flot/jquery.flot.stack.js | 98 +++++++------------ 3 files changed, 51 insertions(+), 83 deletions(-) diff --git a/public/app/plugins/panel/graph/graph_tooltip.js b/public/app/plugins/panel/graph/graph_tooltip.js index 5ae03ccf813..dc573f96a58 100644 --- a/public/app/plugins/panel/graph/graph_tooltip.js +++ b/public/app/plugins/panel/graph/graph_tooltip.js @@ -78,10 +78,9 @@ function ($, _) { } // Highlighting multiple Points depending on the plot type - if (series.lines.steps || series.stack) { - // stacked and steppedLine plots can have series with different length. - // Stacked series can increase its length on each new stacked serie if null points found, - // to speed the index search we begin always on the last found hoverIndex. + if (series.lines.steps) { + // steppedLine plots can have series with different length. + // To speed the index search we begin always on the last found hoverIndex. hoverIndex = this.findHoverIndexFromDataPoints(pos.x, series, hoverIndex); } diff --git a/public/vendor/flot/jquery.flot.js b/public/vendor/flot/jquery.flot.js index ec91314c49d..8433ee26435 100644 --- a/public/vendor/flot/jquery.flot.js +++ b/public/vendor/flot/jquery.flot.js @@ -1201,24 +1201,21 @@ Licensed under the MIT license. points[k + m] = null; } } - else { - // a little bit of line specific stuff that - // perhaps shouldn't be here, but lacking - // better means... - if (insertSteps && k > 0 - && points[k - ps] != null - && points[k - ps] != points[k] - && points[k - ps + 1] != points[k + 1]) { - // copy the point to make room for a middle point - for (m = 0; m < ps; ++m) - points[k + ps + m] = points[k + m]; - // middle point has same y - points[k + 1] = points[k - ps + 1]; + if (insertSteps && k > 0 && (!nullify || points[k-ps] != null)) { + // copy the point to make room for a middle point + for (m = 0; m < ps; ++m) + points[k + ps + m] = points[k + m]; - // we've added a point, better reflect that - k += ps; - } + // middle point has same y + points[k + 1] = points[k - ps + 1] || 0; + + // if series has null values, let's give the last correct value a nice step + if(nullify) + points[k] = p[0]; + + // we've added a point, better reflect that + k += ps; } } } diff --git a/public/vendor/flot/jquery.flot.stack.js b/public/vendor/flot/jquery.flot.stack.js index ffb412ca2a0..22e6954f053 100644 --- a/public/vendor/flot/jquery.flot.stack.js +++ b/public/vendor/flot/jquery.flot.stack.js @@ -78,41 +78,41 @@ charts or filled areas). i = 0, j = 0, l, m; while (true) { - if (i >= points.length) + if (i >= points.length && j >= otherpoints.length) break; l = newpoints.length; + px = points[i + keyOffset]; + py = points[i + accumulateOffset]; + qx = otherpoints[j + keyOffset]; + qy = otherpoints[j + accumulateOffset]; + bottom = 0; - if (points[i] == null) { - // copy gaps + if (i < points.length && px == null) { + // ignore point + i += ps; + } + else if (j < otherpoints.length && qx == null && qy == null) { + // ignore point + j += otherps; + } + else if (i >= points.length) { + // take the remaining points from the previous series + for (m = 0; m < ps; ++m) + newpoints.push(otherpoints[j + m]); + bottom = qy; + j += otherps; + } + else if (j >= otherpoints.length) { + // take the remaining points from the current series for (m = 0; m < ps; ++m) newpoints.push(points[i + m]); i += ps; } - else if (j >= otherpoints.length) { - // for lines, we can't use the rest of the points - if (!withlines) { - for (m = 0; m < ps; ++m) - newpoints.push(points[i + m]); - } - i += ps; - } - else if (otherpoints[j] == null) { - // oops, got a gap - for (m = 0; m < ps; ++m) - newpoints.push(null); - fromgap = true; - j += otherps; - } else { // cases where we actually got two points - px = points[i + keyOffset]; - py = points[i + accumulateOffset]; - qx = otherpoints[j + keyOffset]; - qy = otherpoints[j + accumulateOffset]; - bottom = 0; - - if (px == qx) { + if (px == qx || qx == null) { + // take the point from the current series and skip the previous' one for (m = 0; m < ps; ++m) newpoints.push(points[i + m]); @@ -123,54 +123,26 @@ charts or filled areas). j += otherps; } else if (px > qx) { - // we got past point below, might need to - // insert interpolated extra point - if (withlines && i > 0 && points[i - ps] != null) { - intery = py + (points[i - ps + accumulateOffset] - py) * (qx - px) / (points[i - ps + keyOffset] - px); - newpoints.push(qx); - newpoints.push(intery + qy); - for (m = 2; m < ps; ++m) - newpoints.push(points[i + m]); - bottom = qy; - } + // take the point from the previous series so that the next series can stack over it + for (m = 0; m < ps; ++m) + newpoints.push(otherpoints[j + m]); + + newpoints[l]=null; + bottom = qy; j += otherps; } else { // px < qx - if (fromgap && withlines) { - // if we come from a gap, we just skip this point - i += ps; - continue; - } - + // take the point from the current series for (m = 0; m < ps; ++m) newpoints.push(points[i + m]); - // we might be able to interpolate a point below, - // this can give us a better y - if (withlines && j > 0 && otherpoints[j - otherps] != null) - bottom = qy + (otherpoints[j - otherps + accumulateOffset] - qy) * (px - qx) / (otherpoints[j - otherps + keyOffset] - qx); - - newpoints[l + accumulateOffset] += bottom; - i += ps; } + } - fromgap = false; - - if (l != newpoints.length && withbottom) - newpoints[l + 2] += bottom; - } - - // maintain the line steps invariant - if (withsteps && l != newpoints.length && l > 0 - && newpoints[l] != null - && newpoints[l] != newpoints[l - ps] - && newpoints[l + 1] != newpoints[l - ps + 1]) { - for (m = 0; m < ps; ++m) - newpoints[l + ps + m] = newpoints[l + m]; - newpoints[l + 1] = newpoints[l - ps + 1]; - } + if (l != newpoints.length && withbottom) + newpoints[l + 2] = bottom; } datapoints.points = newpoints; From 43129aa563551e64e720ee93d200c28559a97340 Mon Sep 17 00:00:00 2001 From: benrubson Date: Sat, 15 Oct 2016 13:10:19 +0200 Subject: [PATCH 03/27] Missing values, correct graph specs --- public/app/plugins/panel/graph/specs/graph_specs.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/app/plugins/panel/graph/specs/graph_specs.ts b/public/app/plugins/panel/graph/specs/graph_specs.ts index 9f8d91ca9de..046409b77b2 100644 --- a/public/app/plugins/panel/graph/specs/graph_specs.ts +++ b/public/app/plugins/panel/graph/specs/graph_specs.ts @@ -135,7 +135,7 @@ describe('grafanaGraph', function() { }); it('should set barWidth', function() { - expect(ctx.plotOptions.series.bars.barWidth).to.be(10/1.5); + expect(ctx.plotOptions.series.bars.barWidth).to.be(1/1.5); }); }); From 8d5269dea754a8eeb84f2711ddc2f7fe2e4f4e9b Mon Sep 17 00:00:00 2001 From: benrubson Date: Sat, 15 Oct 2016 13:17:11 +0200 Subject: [PATCH 04/27] Typo --- public/vendor/flot/jquery.flot.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/vendor/flot/jquery.flot.js b/public/vendor/flot/jquery.flot.js index 8433ee26435..e0941bb24c0 100644 --- a/public/vendor/flot/jquery.flot.js +++ b/public/vendor/flot/jquery.flot.js @@ -1202,7 +1202,7 @@ Licensed under the MIT license. } } - if (insertSteps && k > 0 && (!nullify || points[k-ps] != null)) { + if (insertSteps && k > 0 && (!nullify || points[k - ps] != null)) { // copy the point to make room for a middle point for (m = 0; m < ps; ++m) points[k + ps + m] = points[k + m]; From 04a276dc2cc566ec02906070cb93b4d854881aec Mon Sep 17 00:00:00 2001 From: benrubson Date: Sat, 15 Oct 2016 16:51:18 +0200 Subject: [PATCH 05/27] Missing values, correctly highlight last point --- public/app/plugins/panel/graph/graph_tooltip.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/public/app/plugins/panel/graph/graph_tooltip.js b/public/app/plugins/panel/graph/graph_tooltip.js index dc573f96a58..b901c2c8ed6 100644 --- a/public/app/plugins/panel/graph/graph_tooltip.js +++ b/public/app/plugins/panel/graph/graph_tooltip.js @@ -17,7 +17,7 @@ function ($, _) { var initial = last*ps; var len = series.datapoints.points.length; for (var j = initial; j < len; j += ps) { - if (series.datapoints.points[j] > posX) { + if ((series.datapoints.points[initial] != null && series.datapoints.points[j] == null) || series.datapoints.points[j] > posX) { return Math.max(j - ps, 0)/ps; } } @@ -78,9 +78,10 @@ function ($, _) { } // Highlighting multiple Points depending on the plot type - if (series.lines.steps) { - // steppedLine plots can have series with different length. - // To speed the index search we begin always on the last found hoverIndex. + if (series.lines.steps || series.stack) { + // stacked and steppedLine plots can have series with different length. + // Stacked series can increase its length on each new stacked serie if null points found, + // to speed the index search we begin always on the last found hoverIndex. hoverIndex = this.findHoverIndexFromDataPoints(pos.x, series, hoverIndex); } From 07bf4c0b825aa1e109f48376c8c26032e3eeb9fa Mon Sep 17 00:00:00 2001 From: benrubson Date: Sun, 16 Oct 2016 00:29:15 +0200 Subject: [PATCH 06/27] Missing values, keep interpolation --- public/vendor/flot/jquery.flot.stack.js | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/public/vendor/flot/jquery.flot.stack.js b/public/vendor/flot/jquery.flot.stack.js index 22e6954f053..ba2a6b86a2f 100644 --- a/public/vendor/flot/jquery.flot.stack.js +++ b/public/vendor/flot/jquery.flot.stack.js @@ -92,7 +92,7 @@ charts or filled areas). // ignore point i += ps; } - else if (j < otherpoints.length && qx == null && qy == null) { + else if (j < otherpoints.length && qx == null) { // ignore point j += otherps; } @@ -111,7 +111,7 @@ charts or filled areas). } else { // cases where we actually got two points - if (px == qx || qx == null) { + if (px == qx) { // take the point from the current series and skip the previous' one for (m = 0; m < ps; ++m) newpoints.push(points[i + m]); @@ -127,7 +127,10 @@ charts or filled areas). for (m = 0; m < ps; ++m) newpoints.push(otherpoints[j + m]); - newpoints[l]=null; + // we might be able to interpolate + if (i > 0 && points[i - ps] != null) + newpoints[l + accumulateOffset] += py + (points[i - ps + accumulateOffset] - py) * (qx - px) / (points[i - ps + keyOffset] - px); + bottom = qy; j += otherps; @@ -137,6 +140,13 @@ charts or filled areas). for (m = 0; m < ps; ++m) newpoints.push(points[i + m]); + // we might be able to interpolate a point below, + // this can give us a better y + if (j > 0 && otherpoints[j - otherps] != null) + bottom = qy + (otherpoints[j - otherps + accumulateOffset] - qy) * (px - qx) / (otherpoints[j - otherps + keyOffset] - qx); + + newpoints[l + accumulateOffset] += bottom; + i += ps; } } From 604dcbc6b245a42260350da25a8b1694e8f505ef Mon Sep 17 00:00:00 2001 From: benrubson Date: Sun, 16 Oct 2016 00:30:50 +0200 Subject: [PATCH 07/27] Missing values, corect tooltip --- public/app/plugins/panel/graph/graph_tooltip.js | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/public/app/plugins/panel/graph/graph_tooltip.js b/public/app/plugins/panel/graph/graph_tooltip.js index b901c2c8ed6..6477a4a46a9 100644 --- a/public/app/plugins/panel/graph/graph_tooltip.js +++ b/public/app/plugins/panel/graph/graph_tooltip.js @@ -17,7 +17,7 @@ function ($, _) { var initial = last*ps; var len = series.datapoints.points.length; for (var j = initial; j < len; j += ps) { - if ((series.datapoints.points[initial] != null && series.datapoints.points[j] == null) || series.datapoints.points[j] > posX) { + if ((series.datapoints.points[initial] != null && series.datapoints.points[j] == null && ! series.lines.steps) || series.datapoints.points[j] > posX) { return Math.max(j - ps, 0)/ps; } } @@ -61,7 +61,7 @@ function ($, _) { } hoverIndex = this.findHoverIndexFromData(pos.x, series); - hoverDistance = Math.abs(pos.x - series.data[hoverIndex][0]); + hoverDistance = pos.x - series.data[hoverIndex][0]; pointTime = series.data[hoverIndex][0]; if (series.stack) { @@ -102,9 +102,6 @@ function ($, _) { }); } - // Find point which closer to pointer - results.time = _.min(results, 'distance').time; - return results; }; @@ -174,7 +171,7 @@ function ($, _) { continue; } - if (! distance || hoverInfo.distance < distance) { + if (! distance || (hoverInfo.distance >=0 && (hoverInfo.distance < distance || distance < 0)) || (hoverInfo.distance < 0 && hoverInfo.distance > distance)) { distance = hoverInfo.distance; time = hoverInfo.time; } From 9792bd7b08ed21ed8dc7c2de1a1e89684e24aabf Mon Sep 17 00:00:00 2001 From: benrubson Date: Sun, 16 Oct 2016 00:46:56 +0200 Subject: [PATCH 08/27] Typo --- public/app/plugins/panel/graph/graph_tooltip.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/public/app/plugins/panel/graph/graph_tooltip.js b/public/app/plugins/panel/graph/graph_tooltip.js index 6477a4a46a9..f777ba15402 100644 --- a/public/app/plugins/panel/graph/graph_tooltip.js +++ b/public/app/plugins/panel/graph/graph_tooltip.js @@ -2,7 +2,7 @@ define([ 'jquery', 'lodash' ], -function ($, _) { +function ($) { 'use strict'; function GraphTooltip(elem, dashboard, scope, getSeriesFn) { @@ -17,7 +17,8 @@ function ($, _) { var initial = last*ps; var len = series.datapoints.points.length; for (var j = initial; j < len; j += ps) { - if ((series.datapoints.points[initial] != null && series.datapoints.points[j] == null && ! series.lines.steps) || series.datapoints.points[j] > posX) { + if ((series.datapoints.points[initial] != null && series.datapoints.points[j] == null && ! series.lines.steps) + || series.datapoints.points[j] > posX) { return Math.max(j - ps, 0)/ps; } } @@ -171,7 +172,9 @@ function ($, _) { continue; } - if (! distance || (hoverInfo.distance >=0 && (hoverInfo.distance < distance || distance < 0)) || (hoverInfo.distance < 0 && hoverInfo.distance > distance)) { + if (! distance + || (hoverInfo.distance >=0 && (hoverInfo.distance < distance || distance < 0)) + || (hoverInfo.distance < 0 && hoverInfo.distance > distance)) { distance = hoverInfo.distance; time = hoverInfo.time; } From 807bc5eb473eb0cc8d079c36a8de3733b6829731 Mon Sep 17 00:00:00 2001 From: benrubson Date: Sun, 16 Oct 2016 01:09:00 +0200 Subject: [PATCH 09/27] Add time back to result array --- .../app/plugins/panel/graph/graph_tooltip.js | 24 +++++++++++-------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/public/app/plugins/panel/graph/graph_tooltip.js b/public/app/plugins/panel/graph/graph_tooltip.js index f777ba15402..f160c55663f 100644 --- a/public/app/plugins/panel/graph/graph_tooltip.js +++ b/public/app/plugins/panel/graph/graph_tooltip.js @@ -48,6 +48,8 @@ function ($) { //now we know the current X (j) position for X and Y values var last_value = 0; //needed for stacked values + var minDistance, minTime; + for (i = 0; i < seriesList.length; i++) { series = seriesList[i]; @@ -65,6 +67,13 @@ function ($) { hoverDistance = pos.x - series.data[hoverIndex][0]; pointTime = series.data[hoverIndex][0]; + if (! minDistance + || (hoverDistance >=0 && (hoverDistance < minDistance || minDistance < 0)) + || (hoverDistance < 0 && hoverDistance > minDistance)) { + minDistance = hoverDistance; + minTime = pointTime; + } + if (series.stack) { if (panel.tooltip.value_type === 'individual') { value = series.data[hoverIndex][1]; @@ -103,6 +112,9 @@ function ($) { }); } + // Find point which closer to pointer + results.time = minTime; + return results; }; @@ -147,6 +159,8 @@ function ($) { seriesHtml = ''; + absoluteTime = dashboard.formatDate(seriesHoverInfo.time, tooltipFormat); + // Dynamically reorder the hovercard for the current time point if the // option is enabled, sort by yaxis by default. if (panel.tooltip.sort === 2) { @@ -163,8 +177,6 @@ function ($) { }); } - var distance, time; - for (i = 0; i < seriesHoverInfo.length; i++) { hoverInfo = seriesHoverInfo[i]; @@ -172,13 +184,6 @@ function ($) { continue; } - if (! distance - || (hoverInfo.distance >=0 && (hoverInfo.distance < distance || distance < 0)) - || (hoverInfo.distance < 0 && hoverInfo.distance > distance)) { - distance = hoverInfo.distance; - time = hoverInfo.time; - } - var highlightClass = ''; if (item && i === item.seriesIndex) { highlightClass = 'graph-tooltip-list-item--highlight'; @@ -194,7 +199,6 @@ function ($) { plot.highlight(hoverInfo.index, hoverInfo.hoverIndex); } - absoluteTime = dashboard.formatDate(time, tooltipFormat); self.showTooltip(absoluteTime, seriesHtml, pos); } // single series tooltip From d717e0cf13696b5a8c7b64394932ffc6f83addc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Tue, 8 Nov 2016 15:06:07 +0100 Subject: [PATCH 10/27] docs(): minor spelling fix --- docs/sources/alerting/notifications.md | 55 ++++++++------------------ 1 file changed, 16 insertions(+), 39 deletions(-) diff --git a/docs/sources/alerting/notifications.md b/docs/sources/alerting/notifications.md index 18745b222b7..ce9dcdf1b93 100644 --- a/docs/sources/alerting/notifications.md +++ b/docs/sources/alerting/notifications.md @@ -25,31 +25,12 @@ to add and configure a `notification` object. This is done from the Alerting/Not On the notifications list page hit the `New Notification` button to go the the page where you can configure and setup a new notification. -You you specify name and type, and type specific options. You can also test the notification to make +You specify name and type, and type specific options. You can also test the notification to make sure it's working and setup correctly. - - - - - - - - - - - - - - - - - - - ### Send on all alerts -This option will make this notification used for all alert rules, existing and new. +When checked this option will make this notification used for all alert rules, existing and new. ## Supported notification types @@ -61,12 +42,25 @@ To enable email notification you have to setup [SMTP settings](/installation/con in the Grafana config. Email notification will upload an image of the alert graph to an external image destination if available or fallback on attaching the image in the email. +### Slack + +{{< imgbox max-width="40%" img="/img/docs/v4/slack_notification.png" caption="Alerting Slack Notification" >}} + +To set up slack you need to configure an incoming webhook url at slack. You can follow their guide for how +to do that https://api.slack.com/incoming-webhooks If you want to include screenshots of the firing alerts +in the slack messages you have to configure the [external image destination](#external-image-store) in Grafana. + +Setting | Description +---------- | ----------- +Recipient | allows you to override the slack recipient. +Mention | make it possible to include a mention in the slack notification sent by Grafana. Ex @here or @channel + ### Webhook The webhook notification is a simple way to send information about an state change over HTTP to a custom endpoint. Using this notification you could integrated Grafana into any system you choose, by yourself. -Example json schema: +Example json body: ```json { "title": "My alert", @@ -85,19 +79,6 @@ Example json schema: } ``` -### Slack - -{{< imgbox max-width="40%" img="/img/docs/v4/slack_notification.png" caption="Alerting Slack Notification" >}} - -To set up slack you need to configure an incoming webhook url at slack. You can follow their guide for how -to do that https://api.slack.com/incoming-webhooks If you want to include screenshots of the firing alerts -in the slack messages you have to configure the [external image destination](#external-image-store) in Grafana. - -Setting | Description ----------- | ----------- -Recipient | allows you to override the slack recipient. -Mention | make it possible to include a mention in the slack notification sent by Grafana. Ex @here or @channel - ### PagerDuty To set up PagerDuty, all you have to do is to provide an api key. @@ -120,7 +101,3 @@ config file. This is not an optional requirement, you can get slack and email notifications without setting this up. - - - - From 20d789cb94a6f2f69a6d360229cf06d0591ba68f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Tue, 8 Nov 2016 16:15:34 +0100 Subject: [PATCH 11/27] docs(): fix title --- docs/sources/alerting/rules.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/sources/alerting/rules.md b/docs/sources/alerting/rules.md index 823637941da..a30bf885650 100644 --- a/docs/sources/alerting/rules.md +++ b/docs/sources/alerting/rules.md @@ -1,5 +1,5 @@ +++ -title = "Alerting Engine Rules Guide" +title = "Alerting Engine & Rules Guide" description = "Configuring Alert Rules" keywords = ["grafana", "alerting", "guide", "rules"] type = "docs" From 652eb057cfdc7294bb7a77531f3c62925c10db03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Tue, 8 Nov 2016 20:54:26 +0100 Subject: [PATCH 12/27] change(graph): change default line width from 2 to 1 --- public/app/plugins/panel/graph/module.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/app/plugins/panel/graph/module.ts b/public/app/plugins/panel/graph/module.ts index c4b1bd89044..e546a33c30b 100644 --- a/public/app/plugins/panel/graph/module.ts +++ b/public/app/plugins/panel/graph/module.ts @@ -66,7 +66,7 @@ class GraphCtrl extends MetricsPanelCtrl { // fill factor fill : 1, // line width in pixels - linewidth : 2, + linewidth : 1, // show hide points points : false, // point radius in pixels From 2164bbd447a4e4651529e84b178d0d32d0f58cd2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Wed, 9 Nov 2016 10:59:06 +0100 Subject: [PATCH 13/27] ux(tv mode): added 1 second delay animation --- public/sass/components/_view_states.scss | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/public/sass/components/_view_states.scss b/public/sass/components/_view_states.scss index 42a97f5f0bd..8ab13c56776 100644 --- a/public/sass/components/_view_states.scss +++ b/public/sass/components/_view_states.scss @@ -59,34 +59,34 @@ .dashnav-action-icons, .dashnav-move-timeframe { opacity: 0; - transition: opacity 1.5s ease-in-out; + transition: all 1.5s ease-in-out 1s; } // navbar buttons .navbar-brand-btn, .navbar-inner { - border: none; + border-color: transparent; background: transparent; - transition: background 1.5s ease-in-out; + transition: all 1.5s ease-in-out 1s; .fa { opacity: 0; - transition: opacity 1.5s ease-in-out; + transition: all 1.5s ease-in-out 1s; } } .navbar-page-btn { - border: none; - transform: translate3d(-50px, 0, 0); + border-color: transparent; background: transparent; - transition: transform 1.5s ease-in-out; + transform: translate3d(-50px, 0, 0); + transition: all 1.5s ease-in-out 1s; .icon-gf { opacity: 0; - transition: opacity 1.5s ease-in-out; + transition: all 1.5s ease-in-out 1s; } } .gf-timepicker-nav-btn { transform: translate3d(40px, 0, 0); - transition: transform 1.5s ease-in-out; + transition: transform 1.5s ease-in-out 1s; } } From 33664b0a7e76b4b3bc78a3d74a6635a5e78ac2a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Wed, 9 Nov 2016 11:14:04 +0100 Subject: [PATCH 14/27] docs(): minor docs change --- docs/sources/alerting/rules.md | 8 +++++++- public/sass/pages/_alerting.scss | 1 - 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/docs/sources/alerting/rules.md b/docs/sources/alerting/rules.md index a30bf885650..ecab88f48d2 100644 --- a/docs/sources/alerting/rules.md +++ b/docs/sources/alerting/rules.md @@ -73,7 +73,7 @@ in the scenario below. - No new notifications are sent as the alert rule is already in state `Alerting`. So as you can see from the above scenario Grafana will not send out notifications when other series cause the alert -to fire if the rule already is in state ´Alerting`. To improve support for queries that return multiple series +to fire if the rule already is in state `Alerting`. To improve support for queries that return multiple series we plan to track state **per series** in a future release. ### No Data / Null values @@ -107,6 +107,12 @@ The message can contain anything, information about how you might solve the issu The actual notifications are configured and shared between multiple alerts. Read the [Notifications]({{< relref "notifications.md" >}}) guide for how to configure and setup notifications. +## Alert State History & Annotations + +Alert state changes are recorded in the internal annotation table in Grafana's database. The state changes +are visualized as annotations in the alert rule's graph panel. You can also go into the `State history` +submenu in the alert tab to view & clear state history. + ## Troubleshooting {{< imgbox max-width="40%" img="/img/docs/v4/alert_test_rule.png" caption="Test Rule" >}} diff --git a/public/sass/pages/_alerting.scss b/public/sass/pages/_alerting.scss index abe8c775415..b739485f170 100644 --- a/public/sass/pages/_alerting.scss +++ b/public/sass/pages/_alerting.scss @@ -50,7 +50,6 @@ .panel-alert-state { &--alerting { - background-color: mix($critical,$panel-bg, 3%); animation: alerting-panel 1.6s cubic-bezier(1,.1,.73,1) 0s infinite alternate; box-shadow: 0 0 10px rgba($critical,0.5); opacity: 1; From a9c6bdc197076e512ba993bf663ba395d4e1c8d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Wed, 9 Nov 2016 11:34:34 +0100 Subject: [PATCH 15/27] panel(graph): stacking can now handle null values, #6287 --- public/app/plugins/panel/graph/graph.ts | 26 +++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/public/app/plugins/panel/graph/graph.ts b/public/app/plugins/panel/graph/graph.ts index bd9c091f313..239bf9ecc83 100755 --- a/public/app/plugins/panel/graph/graph.ts +++ b/public/app/plugins/panel/graph/graph.ts @@ -183,6 +183,22 @@ module.directive('grafanaGraph', function($rootScope, timeSrv) { } } + function getMinTimeStepOfSeries(data) { + var min = 100000000000; + + for (let i = 0; i < data.length; i++) { + if (!data[i].stats.timeStep) { + continue; + } + + if (data[i].stats.timeStep < min) { + min = data[i].stats.timeStep; + } + } + + return min; + } + // Function for rendering panel function render_panel() { panelWidth = elem.width(); @@ -279,14 +295,8 @@ module.directive('grafanaGraph', function($rootScope, timeSrv) { break; } default: { - var width; - for (let i = 0; i < data.length; i++) { - if (data[i].stats.timeStep && (width == null || (data[i].stats.timeStep / 1.5) < width)) { - width = data[i].stats.timeStep / 1.5; - } - } - if (width) { - options.series.bars.barWidth = width; + if (panel.bars) { + options.series.bars.barWidth = getMinTimeStepOfSeries(data) / 1.5; } addTimeAxis(options); break; From bfed566ea8c950c3ff19f4476a6eb7ed701a4d97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Wed, 9 Nov 2016 11:37:36 +0100 Subject: [PATCH 16/27] changelog(): updated with entry for #2912 and #6287, closes #2912 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 01315d1b773..110c8015a27 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ * **Background Tasks**: Now support automatic purging of old rendered images, closes [#2172](https://github.com/grafana/grafana/issues/2172) * **Dashboard**: After inactivity hide nav/row actions, fade to nice clean view, can be toggled with `d v`, also added kiosk mode, toggled via `d k` [#6476](https://github.com/grafana/grafana/issues/6476) * **Dashboard**: Improved dashboard row menu & add panel UX [#6442](https://github.com/grafana/grafana/issues/6442) +* **Graph Panel**: Support for stacking null values [#2912](https://github.com/grafana/grafana/issues/2912), [#6287](https://github.com/grafana/grafana/issues/6287), thanks @benrubson! ### Breaking changes * **SystemD**: Change systemd description, closes [#5971](https://github.com/grafana/grafana/pull/5971) From d1bedfc1d13884ed0ef61ab51435759d3b1e4bf3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Wed, 9 Nov 2016 12:10:03 +0100 Subject: [PATCH 17/27] updated version to beta1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index e1b009ecf24..a17bf04dbcc 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "company": "Coding Instinct AB" }, "name": "grafana", - "version": "4.0.0-pre1", + "version": "4.0.0-beta1", "repository": { "type": "git", "url": "http://github.com/grafana/grafana.git" From 1d4cace849d0e18915dee6f0e29ace6c4c2ae15c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Wed, 9 Nov 2016 12:52:56 +0100 Subject: [PATCH 18/27] fix(testdata): fixed query in default grafana data source --- .../plugins/datasource/grafana/datasource.ts | 34 +++++++++++++++---- 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/public/app/plugins/datasource/grafana/datasource.ts b/public/app/plugins/datasource/grafana/datasource.ts index 3ae030e4423..c1c0ce6388a 100644 --- a/public/app/plugins/datasource/grafana/datasource.ts +++ b/public/app/plugins/datasource/grafana/datasource.ts @@ -1,17 +1,39 @@ /// +import _ from 'lodash'; + class GrafanaDatasource { /** @ngInject */ constructor(private backendSrv) {} query(options) { - return this.backendSrv.get('/api/metrics/test', { - from: options.range.from.valueOf(), - to: options.range.to.valueOf(), - scenario: 'random_walk', - interval: options.intervalMs, - maxDataPoints: options.maxDataPoints + return this.backendSrv.post('/api/tsdb/query', { + from: options.range.from.valueOf().toString(), + to: options.range.to.valueOf().toString(), + queries: [ + { + "refId": "A", + "scenarioId": "random_walk", + "intervalMs": options.intervalMs, + "maxDataPoints": options.maxDataPoints, + } + ] + }).then(res => { + + var data = []; + if (res.results) { + _.forEach(res.results, queryRes => { + for (let series of queryRes.series) { + data.push({ + target: series.name, + datapoints: series.points + }); + } + }); + } + + return {data: data}; }); } From fff26086d56006afbec16774b8866e19fa3ff2dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Wed, 9 Nov 2016 13:53:20 +0100 Subject: [PATCH 19/27] docs(): updated install links with beta links as well --- docs/sources/installation/debian.md | 15 ++++++++++++--- docs/sources/installation/rpm.md | 18 +++++++++++++++++- docs/sources/installation/windows.md | 4 +++- 3 files changed, 32 insertions(+), 5 deletions(-) diff --git a/docs/sources/installation/debian.md b/docs/sources/installation/debian.md index 2c37ef4d14f..90ebd2053fd 100644 --- a/docs/sources/installation/debian.md +++ b/docs/sources/installation/debian.md @@ -14,13 +14,22 @@ weight = 1 Description | Download ------------ | ------------- -Stable .deb for Debian-based Linux | [3.1.1 (x86-64 deb)](https://grafanarel.s3.amazonaws.com/builds/grafana_3.1.1-1470047149_amd64.deb) +Stable for Debian-based Linux | [3.1.1 (x86-64 deb)](https://grafanarel.s3.amazonaws.com/builds/grafana_3.1.1-1470047149_amd64.deb) +Latest Beta for Debian-based Linux | [4.0.0-beta1 (x86-64 deb)](https://grafanarel.s3.amazonaws.com/builds/grafana_4.0.0-1478693311beta1_amd64.deb) ## Install Stable - $ wget https://grafanarel.s3.amazonaws.com/builds/grafana_3.1.1-1470047149_amd64.deb +``` +$ wget https://grafanarel.s3.amazonaws.com/builds/grafana_3.1.1-1470047149_amd64.deb +$ sudo apt-get install -y adduser libfontconfig +$ sudo dpkg -i grafana_3.1.1-1470047149_amd64.deb +``` + +## Install Latest Beta + + $ wget https://grafanarel.s3.amazonaws.com/builds/grafana_4.0.0-1478693311beta1_amd64.deb $ sudo apt-get install -y adduser libfontconfig - $ sudo dpkg -i grafana_3.1.1-1470047149_amd64.deb + $ sudo dpkg -i grafana_4.0.0-1478693311beta1_amd64.deb ## APT Repository diff --git a/docs/sources/installation/rpm.md b/docs/sources/installation/rpm.md index b23e851182b..cf33e69aa12 100644 --- a/docs/sources/installation/rpm.md +++ b/docs/sources/installation/rpm.md @@ -14,7 +14,8 @@ weight = 2 Description | Download ------------ | ------------- -Stable .RPM for CentOS / Fedora / OpenSuse / Redhat Linux | [3.1.1 (x86-64 rpm)](https://grafanarel.s3.amazonaws.com/builds/grafana-3.1.1-1470047149.x86_64.rpm) +Stable for CentOS / Fedora / OpenSuse / Redhat Linux | [3.1.1 (x86-64 rpm)](https://grafanarel.s3.amazonaws.com/builds/grafana-3.1.1-1470047149.x86_64.rpm) +Latest Beta for CentOS / Fedora / OpenSuse / Redhat Linux | [4.0.0-beta1 (x86-64 rpm)](https://grafanarel.s3.amazonaws.com/builds/grafana-4.0.0-1478693311beta1.x86_64.rpm) ## Install Stable @@ -33,6 +34,21 @@ Or install manually using `rpm`. $ sudo rpm -i --nodeps grafana-3.1.1-1470047149.x86_64.rpm +## Or Install Latest Beta + + $ sudo yum install https://grafanarel.s3.amazonaws.com/builds/grafana-4.0.0-1478693311beta1.x86_64.rpm + +Or install manually using `rpm`. + +#### On CentOS / Fedora / Redhat: + + $ sudo yum install initscripts fontconfig + $ sudo rpm -Uvh grafana-4.0.0-1478693311beta1.x86_64.rpm + +#### On OpenSuse: + + $ sudo rpm -i --nodeps grafana-4.0.0-1478693311beta1.x86_64.rpm + ## Install via YUM Repository Add the following to a new file at `/etc/yum.repos.d/grafana.repo` diff --git a/docs/sources/installation/windows.md b/docs/sources/installation/windows.md index f75f29085c5..537e156e948 100644 --- a/docs/sources/installation/windows.md +++ b/docs/sources/installation/windows.md @@ -13,7 +13,9 @@ weight = 3 Description | Download ------------ | ------------- -Stable Zip package for Windows | [grafana.3.1.1.windows-x64.zip](https://grafanarel.s3.amazonaws.com/winbuilds/dist/grafana-3.1.1.windows-x64.zip) +Latest stable package for Windows | [grafana.3.1.1.windows-x64.zip](https://grafanarel.s3.amazonaws.com/winbuilds/dist/grafana-3.1.1.windows-x64.zip) +Latest beta package for Windows | [grafana.4.0.0-beta1.windows-x64.zip](https://grafanarel.s3.amazonaws.com/builds/grafana-4.0.0-beta1.windows-x64.zip) + ## Configure From 82e1d3f6aa17e5d83b24bd063274152781b72d05 Mon Sep 17 00:00:00 2001 From: bergquist Date: Wed, 9 Nov 2016 14:47:47 +0100 Subject: [PATCH 20/27] feat(influxdb): disable debug logging in appmode --- pkg/tsdb/influxdb/influxdb.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pkg/tsdb/influxdb/influxdb.go b/pkg/tsdb/influxdb/influxdb.go index 3b591342787..959f12f708f 100644 --- a/pkg/tsdb/influxdb/influxdb.go +++ b/pkg/tsdb/influxdb/influxdb.go @@ -11,6 +11,7 @@ import ( "golang.org/x/net/context/ctxhttp" "github.com/grafana/grafana/pkg/log" + "github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/tsdb" ) @@ -50,7 +51,9 @@ func (e *InfluxDBExecutor) Execute(ctx context.Context, queries tsdb.QuerySlice, return result.WithError(err) } - glog.Debug("Influxdb query", "raw query", query) + if setting.Env == setting.DEV { + glog.Debug("Influxdb query", "raw query", query) + } req, err := e.createRequest(query) if err != nil { From 6a8138904e0761b070d110a2862a423d0f0fc9bb Mon Sep 17 00:00:00 2001 From: bergquist Date: Wed, 9 Nov 2016 14:48:51 +0100 Subject: [PATCH 21/27] fix(shortcuts): CTRL on windows or linux --- public/app/core/components/help/help.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/app/core/components/help/help.html b/public/app/core/components/help/help.html index 8482e9df7d1..3356f21d452 100644 --- a/public/app/core/components/help/help.html +++ b/public/app/core/components/help/help.html @@ -22,7 +22,7 @@

mod = - CTRL on windows, CMD key on Mac + CTRL on windows or linux and CMD key on Mac

From ef08a243c5e8a129bb771159537ab0172ef97a93 Mon Sep 17 00:00:00 2001 From: bergquist Date: Thu, 10 Nov 2016 08:23:58 +0100 Subject: [PATCH 22/27] fix(influxdb): fixes possible nil pointer closes #6531 --- pkg/tsdb/influxdb/model_parser.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/tsdb/influxdb/model_parser.go b/pkg/tsdb/influxdb/model_parser.go index ff8977f925b..af727771f1f 100644 --- a/pkg/tsdb/influxdb/model_parser.go +++ b/pkg/tsdb/influxdb/model_parser.go @@ -12,7 +12,6 @@ type InfluxdbQueryParser struct{} func (qp *InfluxdbQueryParser) Parse(model *simplejson.Json, dsInfo *tsdb.DataSourceInfo) (*Query, error) { policy := model.Get("policy").MustString("default") rawQuery := model.Get("query").MustString("") - interval := model.Get("interval").MustString("") measurement := model.Get("measurement").MustString("") @@ -36,7 +35,8 @@ func (qp *InfluxdbQueryParser) Parse(model *simplejson.Json, dsInfo *tsdb.DataSo return nil, err } - if interval == "" { + interval := model.Get("interval").MustString("") + if interval == "" && dsInfo.JsonData != nil { dsInterval := dsInfo.JsonData.Get("timeInterval").MustString("") if dsInterval != "" { interval = dsInterval From 24433263867183637407932573e9883a1946e278 Mon Sep 17 00:00:00 2001 From: Ben RUBSON Date: Thu, 10 Nov 2016 10:19:42 +0100 Subject: [PATCH 23/27] Add some comments about some previous modifications (#6533) --- public/app/plugins/panel/graph/graph.ts | 2 ++ .../app/plugins/panel/graph/graph_tooltip.js | 8 +++++- public/vendor/flot/jquery.flot.js | 2 +- public/vendor/flot/jquery.flot.stack.js | 25 ++++++++++++------- 4 files changed, 26 insertions(+), 11 deletions(-) diff --git a/public/app/plugins/panel/graph/graph.ts b/public/app/plugins/panel/graph/graph.ts index 239bf9ecc83..b2252978d37 100755 --- a/public/app/plugins/panel/graph/graph.ts +++ b/public/app/plugins/panel/graph/graph.ts @@ -183,6 +183,8 @@ module.directive('grafanaGraph', function($rootScope, timeSrv) { } } + // Series could have different timeSteps, + // let's find the smallest one so that bars are correctly rendered. function getMinTimeStepOfSeries(data) { var min = 100000000000; diff --git a/public/app/plugins/panel/graph/graph_tooltip.js b/public/app/plugins/panel/graph/graph_tooltip.js index 697dce0b7ee..3aec815428f 100644 --- a/public/app/plugins/panel/graph/graph_tooltip.js +++ b/public/app/plugins/panel/graph/graph_tooltip.js @@ -21,7 +21,9 @@ function ($) { var initial = last*ps; var len = series.datapoints.points.length; for (var j = initial; j < len; j += ps) { + // Special case of a non stepped line, highlight the very last point just before a null point if ((series.datapoints.points[initial] != null && series.datapoints.points[j] == null && ! series.lines.steps) + //normal case || series.datapoints.points[j] > posX) { return Math.max(j - ps, 0)/ps; } @@ -58,11 +60,13 @@ function ($) { series = seriesList[i]; if (!series.data.length || (panel.legend.hideEmpty && series.allIsNull)) { + // Init value & yaxis so that it does not brake series sorting results.push({ hidden: true, value: 0, yaxis: 0 }); continue; } if (!series.data.length || (panel.legend.hideZero && series.allIsZero)) { + // Init value & yaxis so that it does not brake series sorting results.push({ hidden: true, value: 0, yaxis: 0 }); continue; } @@ -71,6 +75,7 @@ function ($) { hoverDistance = pos.x - series.data[hoverIndex][0]; pointTime = series.data[hoverIndex][0]; + // Take the closest point before the cursor, or if it does not exist, the closest after if (! minDistance || (hoverDistance >=0 && (hoverDistance < minDistance || minDistance < 0)) || (hoverDistance < 0 && hoverDistance > minDistance)) { @@ -99,6 +104,7 @@ function ($) { hoverIndex = this.findHoverIndexFromDataPoints(pos.x, series, hoverIndex); } + // Be sure we have a yaxis so that it does not brake series sorting yaxis = 0; if (series.yaxis) { yaxis = series.yaxis.n; @@ -116,7 +122,7 @@ function ($) { }); } - // Find point which closer to pointer + // Time of the point closer to pointer results.time = minTime; return results; diff --git a/public/vendor/flot/jquery.flot.js b/public/vendor/flot/jquery.flot.js index e0941bb24c0..8d61b3e9b28 100644 --- a/public/vendor/flot/jquery.flot.js +++ b/public/vendor/flot/jquery.flot.js @@ -1210,7 +1210,7 @@ Licensed under the MIT license. // middle point has same y points[k + 1] = points[k - ps + 1] || 0; - // if series has null values, let's give the last correct value a nice step + // if series has null values, let's give the last !null value a nice step if(nullify) points[k] = p[0]; diff --git a/public/vendor/flot/jquery.flot.stack.js b/public/vendor/flot/jquery.flot.stack.js index ba2a6b86a2f..5367ff30603 100644 --- a/public/vendor/flot/jquery.flot.stack.js +++ b/public/vendor/flot/jquery.flot.stack.js @@ -78,9 +78,12 @@ charts or filled areas). i = 0, j = 0, l, m; while (true) { + // browse all points from the current series and from the previous series if (i >= points.length && j >= otherpoints.length) break; + // newpoints will replace current series with + // as many points as different timestamps we have in the 2 (current & previous) series l = newpoints.length; px = points[i + keyOffset]; py = points[i + accumulateOffset]; @@ -89,30 +92,32 @@ charts or filled areas). bottom = 0; if (i < points.length && px == null) { - // ignore point + // let's ignore null points from current series, nothing to do with them i += ps; } else if (j < otherpoints.length && qx == null) { - // ignore point + // let's ignore null points from previous series, nothing to do with them j += otherps; } else if (i >= points.length) { - // take the remaining points from the previous series + // no more points in the current series, simply take the remaining points + // from the previous series so that next series will correctly stack for (m = 0; m < ps; ++m) newpoints.push(otherpoints[j + m]); bottom = qy; j += otherps; } else if (j >= otherpoints.length) { - // take the remaining points from the current series + // no more points in the previous series, of course let's take + // the remaining points from the current series for (m = 0; m < ps; ++m) newpoints.push(points[i + m]); i += ps; } else { - // cases where we actually got two points + // next available points from current and previous series have the same timestamp if (px == qx) { - // take the point from the current series and skip the previous' one + // so take the point from the current series and skip the previous' one for (m = 0; m < ps; ++m) newpoints.push(points[i + m]); @@ -122,8 +127,9 @@ charts or filled areas). i += ps; j += otherps; } + // next available point with the smallest timestamp is from the previous series else if (px > qx) { - // take the point from the previous series so that the next series can stack over it + // so take the point from the previous series so that next series will correctly stack for (m = 0; m < ps; ++m) newpoints.push(otherpoints[j + m]); @@ -135,8 +141,9 @@ charts or filled areas). j += otherps; } - else { // px < qx - // take the point from the current series + // (px < qx) next available point with the smallest timestamp is from the current series + else { + // so of course let's take the point from the current series for (m = 0; m < ps; ++m) newpoints.push(points[i + m]); From 6495ba155af148c0a76eb5c5f254d406edb62cc7 Mon Sep 17 00:00:00 2001 From: Ben RUBSON Date: Thu, 10 Nov 2016 10:20:27 +0100 Subject: [PATCH 24/27] Correct timeStep in case of missing values (#6526) * Correct timeStep in case of missing values * Comment --- public/app/core/time_series2.ts | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/public/app/core/time_series2.ts b/public/app/core/time_series2.ts index d672e0dd0dc..3d99b43704e 100644 --- a/public/app/core/time_series2.ts +++ b/public/app/core/time_series2.ts @@ -115,6 +115,15 @@ export default class TimeSeries { currentValue = this.datapoints[i][0]; currentTime = this.datapoints[i][1]; + // Due to missing values we could have different timeStep all along the series + // so we have to find the minimum one (could occur with aggregators such as ZimSum) + if (i>0) { + var previousTime = this.datapoints[i-1][1]; + if (!this.stats.timeStep || currentTime - previousTime < this.stats.timeStep) { + this.stats.timeStep = currentTime - previousTime; + } + } + if (currentValue === null) { if (ignoreNulls) { continue; } if (nullAsZero) { @@ -145,10 +154,6 @@ export default class TimeSeries { result.push([currentTime, currentValue]); } - if (this.datapoints.length >= 2) { - this.stats.timeStep = this.datapoints[1][1] - this.datapoints[0][1]; - } - if (this.stats.max === -Number.MAX_VALUE) { this.stats.max = null; } if (this.stats.min === Number.MAX_VALUE) { this.stats.min = null; } From 71eb0f3278b63579efe033b058d2363a3df76add Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Thu, 10 Nov 2016 10:50:48 +0100 Subject: [PATCH 25/27] fix(graph): fixed issue with bar width when used in series override, fixes #6528 --- CHANGELOG.md | 9 +++++++-- public/app/core/time_series2.ts | 11 +++++++---- public/app/plugins/panel/graph/graph.ts | 6 ++---- 3 files changed, 16 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 110c8015a27..55298901618 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,9 @@ -# 4.0-pre (unreleased) +# 4.0-beta2 (unrelased) + +### Bugfixes +* **Graph Panel**: Bar width if bars was only used in series override, [#6528](https://github.com/grafana/grafana/issues/6528) + +# 4.0-beta1 (2016-11-09) ### Enhancements * **Login**: Adds option to disable username/password logins, closes [#4674](https://github.com/grafana/grafana/issues/4674) @@ -24,7 +29,7 @@ * **SystemD**: Change systemd description, closes [#5971](https://github.com/grafana/grafana/pull/5971) * **lodash upgrade**: Upgraded lodash from 2.4.2 to 4.15.0, this contains a number of breaking changes that could effect plugins. closes [#6021](https://github.com/grafana/grafana/pull/6021) -### Bugfixes +### Bug fixes * **Table Panel**: Fixed problem when switching to Mixed datasource in metrics tab, fixes [#5999](https://github.com/grafana/grafana/pull/5999) * **Playlist**: Fixed problem with play order not matching order defined in playlist, fixes [#5467](https://github.com/grafana/grafana/pull/5467) * **Graph panel**: Fixed problem with auto decimals on y axis when datamin=datamax, fixes [#6070](https://github.com/grafana/grafana/pull/6070) diff --git a/public/app/core/time_series2.ts b/public/app/core/time_series2.ts index 3d99b43704e..25a99a38ad8 100644 --- a/public/app/core/time_series2.ts +++ b/public/app/core/time_series2.ts @@ -102,6 +102,7 @@ export default class TimeSeries { this.stats.min = Number.MAX_VALUE; this.stats.avg = null; this.stats.current = null; + this.stats.timeStep = Number.MAX_VALUE; this.allIsNull = true; this.allIsZero = true; @@ -110,6 +111,7 @@ export default class TimeSeries { var currentTime; var currentValue; var nonNulls = 0; + var previousTime; for (var i = 0; i < this.datapoints.length; i++) { currentValue = this.datapoints[i][0]; @@ -117,12 +119,13 @@ export default class TimeSeries { // Due to missing values we could have different timeStep all along the series // so we have to find the minimum one (could occur with aggregators such as ZimSum) - if (i>0) { - var previousTime = this.datapoints[i-1][1]; - if (!this.stats.timeStep || currentTime - previousTime < this.stats.timeStep) { - this.stats.timeStep = currentTime - previousTime; + if (previousTime !== undefined) { + let timeStep = currentTime - previousTime; + if (timeStep < this.stats.timeStep) { + this.stats.timeStep = timeStep; } } + previousTime = currentTime; if (currentValue === null) { if (ignoreNulls) { continue; } diff --git a/public/app/plugins/panel/graph/graph.ts b/public/app/plugins/panel/graph/graph.ts index b2252978d37..14e0d5c99c0 100755 --- a/public/app/plugins/panel/graph/graph.ts +++ b/public/app/plugins/panel/graph/graph.ts @@ -186,7 +186,7 @@ module.directive('grafanaGraph', function($rootScope, timeSrv) { // Series could have different timeSteps, // let's find the smallest one so that bars are correctly rendered. function getMinTimeStepOfSeries(data) { - var min = 100000000000; + var min = Number.MAX_VALUE; for (let i = 0; i < data.length; i++) { if (!data[i].stats.timeStep) { @@ -297,9 +297,7 @@ module.directive('grafanaGraph', function($rootScope, timeSrv) { break; } default: { - if (panel.bars) { - options.series.bars.barWidth = getMinTimeStepOfSeries(data) / 1.5; - } + options.series.bars.barWidth = getMinTimeStepOfSeries(data) / 1.5; addTimeAxis(options); break; } From 4f3c8c666323b95a22df87df1a2e85e7b4b9ce60 Mon Sep 17 00:00:00 2001 From: bergquist Date: Thu, 10 Nov 2016 10:41:00 +0100 Subject: [PATCH 26/27] fix(influxdb): add default operator ref #6523 --- pkg/tsdb/influxdb/query_builder.go | 36 ++++++++++++++++++------- pkg/tsdb/influxdb/query_builder_test.go | 28 +++++++++++++++---- 2 files changed, 49 insertions(+), 15 deletions(-) diff --git a/pkg/tsdb/influxdb/query_builder.go b/pkg/tsdb/influxdb/query_builder.go index a4b122cbbd2..b783ff5c603 100644 --- a/pkg/tsdb/influxdb/query_builder.go +++ b/pkg/tsdb/influxdb/query_builder.go @@ -5,9 +5,15 @@ import ( "strconv" "strings" + "regexp" + "github.com/grafana/grafana/pkg/tsdb" ) +var ( + regexpOperatorPattern *regexp.Regexp = regexp.MustCompile(`^\/.*\/$`) +) + type QueryBuilder struct{} func (qb *QueryBuilder) Build(query *Query, queryContext *tsdb.QueryContext) (string, error) { @@ -43,18 +49,28 @@ func (qb *QueryBuilder) renderTags(query *Query) []string { str += " " } - value := tag.Value - nValue, err := strconv.ParseFloat(tag.Value, 64) - - if tag.Operator == "=~" || tag.Operator == "!~" { - value = fmt.Sprintf("%s", value) - } else if err == nil { - value = fmt.Sprintf("%v", nValue) - } else { - value = fmt.Sprintf("'%s'", value) + //If the operator is missing we fall back to sensible defaults + if tag.Operator == "" { + if regexpOperatorPattern.Match([]byte(tag.Value)) { + tag.Operator = "=~" + } else { + tag.Operator = "=" + } } - res = append(res, fmt.Sprintf(`%s"%s" %s %s`, str, tag.Key, tag.Operator, value)) + textValue := "" + numericValue, err := strconv.ParseFloat(tag.Value, 64) + + // quote value unless regex or number + if tag.Operator == "=~" || tag.Operator == "!~" { + textValue = tag.Value + } else if err == nil { + textValue = fmt.Sprintf("%v", numericValue) + } else { + textValue = fmt.Sprintf("'%s'", tag.Value) + } + + res = append(res, fmt.Sprintf(`%s"%s" %s %s`, str, tag.Key, tag.Operator, textValue)) } return res diff --git a/pkg/tsdb/influxdb/query_builder_test.go b/pkg/tsdb/influxdb/query_builder_test.go index d13fe3a3c6f..408db18e549 100644 --- a/pkg/tsdb/influxdb/query_builder_test.go +++ b/pkg/tsdb/influxdb/query_builder_test.go @@ -86,16 +86,34 @@ func TestInfluxdbQueryBuilder(t *testing.T) { So(rawQuery, ShouldEqual, `Raw query`) }) - Convey("can render regex tags", func() { - query := &Query{Tags: []*Tag{&Tag{Operator: "=~", Value: "value", Key: "key"}}} + Convey("can render normal tags without operator", func() { + query := &Query{Tags: []*Tag{&Tag{Operator: "", Value: `value`, Key: "key"}}} - So(strings.Join(builder.renderTags(query), ""), ShouldEqual, `"key" =~ value`) + So(strings.Join(builder.renderTags(query), ""), ShouldEqual, `"key" = 'value'`) + }) + + Convey("can render regex tags without operator", func() { + query := &Query{Tags: []*Tag{&Tag{Operator: "", Value: `/value/`, Key: "key"}}} + + So(strings.Join(builder.renderTags(query), ""), ShouldEqual, `"key" =~ /value/`) + }) + + Convey("can render regex tags", func() { + query := &Query{Tags: []*Tag{&Tag{Operator: "=~", Value: `/value/`, Key: "key"}}} + + So(strings.Join(builder.renderTags(query), ""), ShouldEqual, `"key" =~ /value/`) }) Convey("can render number tags", func() { - query := &Query{Tags: []*Tag{&Tag{Operator: "=", Value: "1", Key: "key"}}} + query := &Query{Tags: []*Tag{&Tag{Operator: "=", Value: "10001", Key: "key"}}} - So(strings.Join(builder.renderTags(query), ""), ShouldEqual, `"key" = 1`) + So(strings.Join(builder.renderTags(query), ""), ShouldEqual, `"key" = 10001`) + }) + + Convey("can render number tags with decimals", func() { + query := &Query{Tags: []*Tag{&Tag{Operator: "=", Value: "10001.1", Key: "key"}}} + + So(strings.Join(builder.renderTags(query), ""), ShouldEqual, `"key" = 10001.1`) }) Convey("can render string tags", func() { From 70b9ba257357628efec51a81f09070f6f30a43b3 Mon Sep 17 00:00:00 2001 From: bergquist Date: Thu, 10 Nov 2016 11:30:14 +0100 Subject: [PATCH 27/27] tech(build): switch to golang 1.7.3 tls/cipher fixes https://github.com/golang/go/issues?q=milestone%3AGo1.7.3 --- circle.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/circle.yml b/circle.yml index 39dc626df04..2422715bda3 100644 --- a/circle.yml +++ b/circle.yml @@ -5,7 +5,7 @@ machine: GOPATH: "/home/ubuntu/.go_workspace" ORG_PATH: "github.com/grafana" REPO_PATH: "${ORG_PATH}/grafana" - GODIST: "go1.7.1.linux-amd64.tar.gz" + GODIST: "go1.7.3.linux-amd64.tar.gz" post: - mkdir -p download - test -e download/$GODIST || curl -o download/$GODIST https://storage.googleapis.com/golang/$GODIST