diff --git a/CHANGELOG.md b/CHANGELOG.md index 1fb7ec258ea..69835953dc1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ # 1.8.0 (unreleased) +**Fixes** +- [Issue #802](https://github.com/grafana/grafana/issues/802). Annotations: Fix when using InfluxDB datasource +- [Issue #795](https://github.com/grafana/grafana/issues/795). Chrome: Fix for display issue in chrome beta & chrome canary when entering edit mode +- [Issue #818](https://github.com/grafana/grafana/issues/818). Graph: Added percent y-axis format +- [Issue #828](https://github.com/grafana/grafana/issues/828). Elasticsearch: saving new dashboard with title equal to slugified url would cause it to deleted. +- [Issue #830](https://github.com/grafana/grafana/issues/830). Annotations: Fix for elasticsearch annotations and mapping nested fields + +# 1.8.0-RC1 (2014-09-12) + **UI polish / changes** - [Issue #725](https://github.com/grafana/grafana/issues/725). UI: All modal editors are removed and replaced by an edit pane under menu. The look of editors is also updated and polished. Search dropdown is also shown as pane under menu and has seen some UI polish. @@ -13,6 +22,8 @@ - [Issue #262](https://github.com/grafana/grafana/issues/262). Templating: Ability to use template variables for function parameters via custom variable type, can be used as parameter for movingAverage or scaleToSeconds for example - [Issue #312](https://github.com/grafana/grafana/issues/312). Templating: Can now use template variables in panel titles - [Issue #613](https://github.com/grafana/grafana/issues/613). Templating: Full support for InfluxDB, filter by part of series names, extract series substrings, nested queries, multipe where clauses! +- Template variables can be initialized from url, with var-my_varname=value, breaking change, before it was just my_varname. +- Templating and url state sync has some issues that are not solved for this release, see [Issue #772](https://github.com/grafana/grafana/issues/772) for more details. **InfluxDB Breaking changes** - To better support templating, fill(0) and group by time low limit some changes has been made to the editor and query model schema @@ -41,6 +52,9 @@ - [Issue #425](https://github.com/grafana/grafana/issues/425). Graph: New section in 'Display Styles' tab to override any display setting on per series bases (mix and match lines, bars, points, fill, stack, line width etc) - [Issue #634](https://github.com/grafana/grafana/issues/634). Dashboard: Dashboard tags now in different colors (from fixed palette) determined by tag name. - [Issue #685](https://github.com/grafana/grafana/issues/685). Dashboard: New config.js option to change/remove window title prefix. +- [Issue #781](https://github.com/grafana/grafana/issues/781). Dashboard: Title URL is now slugified for greater URL readability, works with both ES & InfluxDB storage, is backward compatible +- [Issue #785](https://github.com/grafana/grafana/issues/785). Elasticsearch: Support for full elasticsearch lucene search grammar when searching for dashboards, better async search +- [Issue #787](https://github.com/grafana/grafana/issues/787). Dashboard: time range can now be read from URL parameters, will override dashboard saved time range **Fixes** - [Issue #696](https://github.com/grafana/grafana/issues/696). Graph: Fix for y-axis format 'none' when values are in scientific notation (ex 2.3e-13) @@ -234,7 +248,7 @@ Read this for more info: - More graphite function definitions - Make "ms" axis format include hour, day, weeks, month and year ([Issue #149](https://github.com/grafana/grafana/issues/149)) - Microsecond axis format ([Issue #146](https://github.com/grafana/grafana/issues/146)) -- Specify template paramaters in URL ([Issue #123](https://github.com/grafana/grafana/issues/123)) +- Specify template parameters in URL ([Issue #123](https://github.com/grafana/grafana/issues/123)) ### Fixes - Basic Auth fix ([Issue #152](https://github.com/grafana/grafana/issues/152)) diff --git a/latest.json b/latest.json index 38e862a643a..c66de1bc179 100644 --- a/latest.json +++ b/latest.json @@ -1,4 +1,4 @@ { - "version": "1.7.0", - "url": "http://grafanarel.s3.amazonaws.com/grafana-1.7.0" + "version": "1.8.0-rc1", + "url": "http://grafanarel.s3.amazonaws.com/grafana-1.8.0-rc1" } diff --git a/package.json b/package.json index 8de8f05cf8a..f84ca9cf8c6 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "company": "Coding Instinct AB" }, "name": "grafana", - "version": "1.8.0", + "version": "1.8.0-rc1", "repository": { "type": "git", "url": "http://github.com/torkelo/grafana.git" diff --git a/src/app/components/kbn.js b/src/app/components/kbn.js index 35e5c2f67a7..7c61b22c83b 100644 --- a/src/app/components/kbn.js +++ b/src/app/components/kbn.js @@ -531,6 +531,10 @@ function($, _, moment) { return function(val) { return kbn.nanosFormat(val, decimals); }; + case 'percent': + return function(val, axis) { + return kbn.noneFormat(val, axis ? axis.tickDecimals : null) + ' %'; + }; default: return function(val, axis) { return kbn.noneFormat(val, axis ? axis.tickDecimals : null); @@ -563,8 +567,8 @@ function($, _, moment) { kbn.msFormat = function(size, decimals) { // Less than 1 milli, downscale to micro - if (Math.abs(size) < 1) { - return kbn.microsFormat(size * 1000,decimals); + if (size !== 0 && Math.abs(size) < 1) { + return kbn.microsFormat(size * 1000, decimals); } else if (Math.abs(size) < 1000) { return size.toFixed(decimals) + " ms"; @@ -591,7 +595,7 @@ function($, _, moment) { kbn.sFormat = function(size, decimals) { // Less than 1 sec, downscale to milli - if (Math.abs(size) < 1) { + if (size !== 0 && Math.abs(size) < 1) { return kbn.msFormat(size * 1000, decimals); } // Less than 10 min, use seconds @@ -620,7 +624,7 @@ function($, _, moment) { kbn.microsFormat = function(size, decimals) { // Less than 1 micro, downscale to nano - if (Math.abs(size) < 1) { + if (size !== 0 && Math.abs(size) < 1) { return kbn.nanosFormat(size * 1000, decimals); } else if (Math.abs(size) < 1000) { @@ -655,6 +659,13 @@ function($, _, moment) { } }; + kbn.slugifyForUrl = function(str) { + return str + .toLowerCase() + .replace(/[^\w ]+/g,'') + .replace(/ +/g,'-'); + }; + kbn.stringToJsRegex = function(str) { if (str[0] !== '/') { return new RegExp(str); diff --git a/src/app/controllers/dashboardCtrl.js b/src/app/controllers/dashboardCtrl.js index 8ccbb170d8a..424f0e225e8 100644 --- a/src/app/controllers/dashboardCtrl.js +++ b/src/app/controllers/dashboardCtrl.js @@ -44,14 +44,14 @@ function (angular, $, config, _) { $scope.setupDashboard = function(event, dashboardData) { $rootScope.performance.dashboardLoadStart = new Date().getTime(); $rootScope.performance.panelsInitialized = 0; - $rootScope.performance.panelsRendered= 0; + $rootScope.performance.panelsRendered = 0; $scope.dashboard = dashboardSrv.create(dashboardData); $scope.dashboardViewState = dashboardViewStateSrv.create($scope); // init services timeSrv.init($scope.dashboard); - templateValuesSrv.init($scope.dashboard); + templateValuesSrv.init($scope.dashboard, $scope.dashboardViewState); panelMoveSrv.init($scope.dashboard, $scope); $scope.checkFeatureToggles(); @@ -93,18 +93,18 @@ function (angular, $, config, _) { }; }; - $scope.panel_path =function(type) { - if(type) { - return 'app/panels/'+type.replace(".","/"); + $scope.edit_path = function(type) { + var p = $scope.panel_path(type); + if(p) { + return p+'/editor.html'; } else { return false; } }; - $scope.edit_path = function(type) { - var p = $scope.panel_path(type); - if(p) { - return p+'/editor.html'; + $scope.panel_path =function(type) { + if(type) { + return 'app/panels/'+type.replace(".","/"); } else { return false; } diff --git a/src/app/controllers/dashboardNavCtrl.js b/src/app/controllers/dashboardNavCtrl.js index b6813b7ce19..76ef34f8e92 100644 --- a/src/app/controllers/dashboardNavCtrl.js +++ b/src/app/controllers/dashboardNavCtrl.js @@ -78,7 +78,7 @@ function (angular, _, moment, config, store) { var clone = angular.copy($scope.dashboard); $scope.db.saveDashboard(clone) .then(function(result) { - alertSrv.set('Dashboard Saved', 'Dashboard has been saved as "' + result.title + '"','success', 5000); + alertSrv.set('Dashboard Saved', 'Saved as "' + result.title + '"','success', 3000); if (result.url !== $location.path()) { $location.search({}); @@ -88,7 +88,7 @@ function (angular, _, moment, config, store) { $rootScope.$emit('dashboard-saved', $scope.dashboard); }, function(err) { - alertSrv.set('Save failed', err, 'error',5000); + alertSrv.set('Save failed', err, 'error', 5000); }); }; diff --git a/src/app/controllers/graphiteTarget.js b/src/app/controllers/graphiteTarget.js index cd13926cc80..27299474bc0 100644 --- a/src/app/controllers/graphiteTarget.js +++ b/src/app/controllers/graphiteTarget.js @@ -15,7 +15,7 @@ function (angular, _, config, gfunc, Parser) { $scope.init = function() { $scope.target.target = $scope.target.target || ''; - $scope.targetLetter = targetLetters[$scope.$index]; + $scope.targetLetters = targetLetters; parseTarget(); }; @@ -90,7 +90,7 @@ function (angular, _, config, gfunc, Parser) { break; case 'metric': if ($scope.segments.length > 0) { - if ($scope.segments[0].length !== 1) { + if (astNode.segments.length !== 1) { throw { message: 'Multiple metric params not supported, use text editor.' }; } addFunctionParameter(func, astNode.segments[0].value, index, true); diff --git a/src/app/controllers/search.js b/src/app/controllers/search.js index c294c9d86f6..18b860d0ab5 100644 --- a/src/app/controllers/search.js +++ b/src/app/controllers/search.js @@ -17,6 +17,7 @@ function (angular, _, config, $) { $scope.results = {dashboards: [], tags: [], metrics: []}; $scope.query = { query: 'title:' }; $scope.db = datasourceSrv.getGrafanaDB(); + $scope.currentSearchId = 0; $timeout(function() { $scope.giveSearchFocus = $scope.giveSearchFocus + 1; @@ -75,8 +76,18 @@ function (angular, _, config, $) { }; $scope.searchDashboards = function(queryString) { + // bookeeping for determining stale search requests + var searchId = $scope.currentSearchId + 1; + $scope.currentSearchId = searchId > $scope.currentSearchId ? searchId : $scope.currentSearchId; + return $scope.db.searchDashboards(queryString) .then(function(results) { + // since searches are async, it's possible that these results are not for the latest search. throw + // them away if so + if (searchId < $scope.currentSearchId) { + return; + } + $scope.tagsOnly = results.tagsOnly; $scope.results.dashboards = results.dashboards; $scope.results.tags = results.tags; @@ -108,9 +119,10 @@ function (angular, _, config, $) { $scope.searchDashboards($scope.query.query); }; - $scope.deleteDashboard = function(id, evt) { + $scope.deleteDashboard = function(dash, evt) { evt.stopPropagation(); - $scope.emitAppEvent('delete-dashboard', { id: id }); + $scope.emitAppEvent('delete-dashboard', { id: dash.id }); + $scope.results.dashboards = _.without($scope.results.dashboards, dash); }; $scope.addMetricToCurrentDashboard = function (metricId) { diff --git a/src/app/dashboards/default.json b/src/app/dashboards/default.json index 015ede1d7dc..70b907e13da 100644 --- a/src/app/dashboards/default.json +++ b/src/app/dashboards/default.json @@ -9,14 +9,15 @@ "title": "New row", "height": "150px", "collapse": false, + "editable": true, "panels": [ { - "error": false, + "id": 1, "span": 12, "editable": true, "type": "text", "mode": "html", - "content": "
\n \n
", + "content": "
\n \n
", "style": {}, "title": "Welcome to" } @@ -26,22 +27,20 @@ "title": "Welcome to Grafana", "height": "210px", "collapse": false, + "editable": true, "panels": [ { - "error": false, + "id": 2, "span": 6, - "editable": true, "type": "text", - "loadingEditor": false, "mode": "html", "content": "
\n\n
\n
\n \n
\n
\n \n
\n
", "style": {}, "title": "Documentation Links" }, { - "error": false, + "id": 3, "span": 6, - "editable": true, "type": "text", "mode": "html", "content": "
\n\n
\n
\n \n
\n
\n", @@ -53,11 +52,12 @@ { "title": "test", "height": "250px", + "editable": true, "collapse": false, "panels": [ { + "id": 4, "span": 12, - "editable": true, "type": "graph", "x-axis": true, "y-axis": true, diff --git a/src/app/dashboards/scripted_async.js b/src/app/dashboards/scripted_async.js index 84e5d976f6b..31d23f2dde2 100644 --- a/src/app/dashboards/scripted_async.js +++ b/src/app/dashboards/scripted_async.js @@ -35,11 +35,9 @@ return function(callback) { // Set a title dashboard.title = 'Scripted dash'; - dashboard.services.filter = { - time: { - from: "now-" + (ARGS.from || timspan), - to: "now" - } + dashboard.time = { + from: "now-" + (ARGS.from || timspan), + to: "now" }; var rows = 1; @@ -78,4 +76,4 @@ return function(callback) { callback(dashboard); }); -} \ No newline at end of file +} diff --git a/src/app/dashboards/template_vars.json b/src/app/dashboards/template_vars.json new file mode 100644 index 00000000000..affe7727ce2 --- /dev/null +++ b/src/app/dashboards/template_vars.json @@ -0,0 +1,264 @@ +{ + "id": null, + "title": "Templated Graphs Nested", + "originalTitle": "Templated Graphs Nested", + "tags": [ + "showcase", + "templated" + ], + "style": "dark", + "timezone": "browser", + "editable": true, + "hideControls": false, + "rows": [ + { + "title": "Row1", + "height": "350px", + "editable": true, + "collapse": false, + "collapsable": true, + "panels": [ + { + "span": 12, + "editable": true, + "type": "graph", + "loadingEditor": false, + "datasource": null, + "renderer": "flot", + "x-axis": true, + "y-axis": true, + "scale": 1, + "y_formats": [ + "short", + "short" + ], + "grid": { + "max": null, + "min": 0, + "threshold1": null, + "threshold2": null, + "threshold1Color": "rgba(216, 200, 27, 0.27)", + "threshold2Color": "rgba(234, 112, 112, 0.22)", + "leftMax": null, + "rightMax": null, + "leftMin": null, + "rightMin": null + }, + "annotate": { + "enable": false + }, + "resolution": 100, + "lines": true, + "fill": 1, + "linewidth": 1, + "points": false, + "pointradius": 5, + "bars": false, + "stack": true, + "legend": { + "show": true, + "values": false, + "min": false, + "max": false, + "current": false, + "total": false, + "avg": false + }, + "percentage": false, + "zerofill": true, + "nullPointMode": "connected", + "steppedLine": false, + "tooltip": { + "value_type": "cumulative", + "query_as_alias": true + }, + "targets": [ + { + "target": "aliasByNode(apps.$app.$server.counters.requests.count, 2)", + "function": "mean", + "column": "value" + } + ], + "aliasColors": { + "highres.test": "#1F78C1", + "scale(highres.test,3)": "#6ED0E0", + "mobile": "#6ED0E0", + "tablet": "#EAB839" + }, + "title": "Traffic [[period]]", + "id": 1, + "seriesOverrides": [] + } + ], + "notice": false + }, + { + "title": "Row1", + "height": "350px", + "editable": true, + "collapse": false, + "collapsable": true, + "panels": [ + { + "span": 12, + "editable": true, + "type": "graph", + "loadingEditor": false, + "datasource": null, + "renderer": "flot", + "x-axis": true, + "y-axis": true, + "scale": 1, + "y_formats": [ + "short", + "short" + ], + "grid": { + "max": null, + "min": 0, + "threshold1": null, + "threshold2": null, + "threshold1Color": "rgba(216, 200, 27, 0.27)", + "threshold2Color": "rgba(234, 112, 112, 0.22)", + "leftMax": null, + "rightMax": null, + "leftMin": null, + "rightMin": null + }, + "annotate": { + "enable": false + }, + "resolution": 100, + "lines": true, + "fill": 1, + "linewidth": 1, + "points": false, + "pointradius": 5, + "bars": false, + "stack": true, + "legend": { + "show": true, + "values": false, + "min": false, + "max": false, + "current": false, + "total": false, + "avg": false + }, + "percentage": false, + "zerofill": true, + "nullPointMode": "connected", + "steppedLine": false, + "tooltip": { + "value_type": "cumulative", + "query_as_alias": true + }, + "targets": [ + { + "target": "aliasByNode(apps.$app.$server.counters.requests.count, 2)" + } + ], + "aliasColors": { + "highres.test": "#1F78C1", + "scale(highres.test,3)": "#6ED0E0", + "mobile": "#6ED0E0", + "tablet": "#EAB839" + }, + "title": "Second pannel", + "id": 2, + "seriesOverrides": [] + } + ], + "notice": false + } + ], + "nav": [ + { + "type": "timepicker", + "collapse": false, + "notice": false, + "enable": true, + "status": "Stable", + "time_options": [ + "5m", + "15m", + "1h", + "6h", + "12h", + "24h", + "2d", + "7d", + "30d" + ], + "refresh_intervals": [ + "5s", + "10s", + "30s", + "1m", + "5m", + "15m", + "30m", + "1h", + "2h", + "1d" + ], + "now": true + } + ], + "time": { + "from": "now-15m", + "to": "now" + }, + "templating": { + "list": [ + { + "type": "query", + "name": "app", + "query": "apps.*", + "includeAll": true, + "options": [], + "current": { + "text": "All", + "value": "*" + }, + "datasource": null, + "allFormat": "wildcard", + "refresh": true + }, + { + "type": "query", + "name": "server", + "query": "apps.$app.*", + "includeAll": true, + "options": [], + "current": { + "text": "All", + "value": "*" + }, + "datasource": null, + "allFormat": "Glob", + "refresh": false + }, + { + "type": "query", + "datasource": null, + "refresh_on_load": false, + "name": "metric", + "options": [], + "includeAll": true, + "allFormat": "glob", + "query": "apps.$app.$server.*", + "current": { + "text": "counters", + "value": "counters" + } + } + ], + "enable": true + }, + "annotations": { + "enable": false + }, + "refresh": false, + "version": 6 +} diff --git a/src/app/directives/grafanaGraph.js b/src/app/directives/grafanaGraph.js index 9bb302bf671..15f1556aa62 100755 --- a/src/app/directives/grafanaGraph.js +++ b/src/app/directives/grafanaGraph.js @@ -15,7 +15,7 @@ function (angular, $, kbn, moment, _) { restrict: 'A', template: '
', link: function(scope, elem) { - var data, plot, annotations; + var data, annotations; var hiddenData = {}; var dashboard = scope.dashboard; var legendSideLastValue = null; @@ -82,6 +82,10 @@ function (angular, $, kbn, moment, _) { render_panel_as_graphite_png(data); return true; } + + if (elem.width() === 0) { + return; + } } // Function for rendering panel @@ -165,18 +169,22 @@ function (angular, $, kbn, moment, _) { var sortedSeries = _.sortBy(data, function(series) { return series.zindex; }); - // if legend is to the right delay plot draw a few milliseconds - // so the legend width calculation can be done + function callPlot() { + try { + $.plot(elem, sortedSeries, options); + } catch (e) { + console.log('flotcharts error', e); + } + + addAxisLabels(); + } + if (shouldDelayDraw(panel)) { + setTimeout(callPlot, 50); legendSideLastValue = panel.legend.rightSide; - setTimeout(function() { - plot = $.plot(elem, sortedSeries, options); - addAxisLabels(); - }, 50); } else { - plot = $.plot(elem, sortedSeries, options); - addAxisLabels(); + callPlot(); } } diff --git a/src/app/directives/graphiteSegment.js b/src/app/directives/graphiteSegment.js index 032ea99c39a..f0116bc8847 100644 --- a/src/app/directives/graphiteSegment.js +++ b/src/app/directives/graphiteSegment.js @@ -94,7 +94,7 @@ function (angular, app, _, $) { }; $input.attr('data-provide', 'typeahead'); - $input.typeahead({ source: $scope.source, minLength: 0, items: 100, updater: $scope.updater }); + $input.typeahead({ source: $scope.source, minLength: 0, items: 10000, updater: $scope.updater }); var typeahead = $input.data('typeahead'); typeahead.lookup = function () { diff --git a/src/app/directives/tip.js b/src/app/directives/tip.js index 975a4c02228..974ed98a637 100644 --- a/src/app/directives/tip.js +++ b/src/app/directives/tip.js @@ -11,10 +11,10 @@ function (angular, kbn) { return { restrict: 'E', link: function(scope, elem, attrs) { - var _t = ''; elem.replaceWith($compile(angular.element(_t))(scope)); } }; }); -}); \ No newline at end of file +}); diff --git a/src/app/filters/all.js b/src/app/filters/all.js index b2729d120dd..eb9a736d0ce 100755 --- a/src/app/filters/all.js +++ b/src/app/filters/all.js @@ -57,7 +57,7 @@ define(['angular', 'jquery', 'lodash', 'moment'], function (angular, $, _, momen module.filter('interpolateTemplateVars', function(templateSrv) { return function(text) { - return templateSrv.replace(text); + return templateSrv.replaceWithText(text); }; }); diff --git a/src/app/panels/graph/axisEditor.html b/src/app/panels/graph/axisEditor.html index 6800ee832b9..9380cb99327 100644 --- a/src/app/panels/graph/axisEditor.html +++ b/src/app/panels/graph/axisEditor.html @@ -4,7 +4,7 @@
Left Y Axis
- +
@@ -23,7 +23,7 @@
Right Y Axis
- +
diff --git a/src/app/panels/graph/seriesOverridesCtrl.js b/src/app/panels/graph/seriesOverridesCtrl.js index 4f54c6d0e6f..1b6b1dfc144 100644 --- a/src/app/panels/graph/seriesOverridesCtrl.js +++ b/src/app/panels/graph/seriesOverridesCtrl.js @@ -70,7 +70,7 @@ define([ $scope.addOverrideOption('Staircase line', 'steppedLine', [true, false]); $scope.addOverrideOption('Points', 'points', [true, false]); $scope.addOverrideOption('Points Radius', 'pointradius', [1,2,3,4,5]); - $scope.addOverrideOption('Stack', 'stack', [true, false]); + $scope.addOverrideOption('Stack', 'stack', [true, false, 2, 3, 4, 5]); $scope.addOverrideOption('Y-axis', 'yaxis', [1, 2]); $scope.addOverrideOption('Z-index', 'zindex', [-1,-2,-3,0,1,2,3]); $scope.updateCurrentOverrides(); diff --git a/src/app/panels/graph/styleEditor.html b/src/app/panels/graph/styleEditor.html index b8c80473ebd..cd83f23f197 100644 --- a/src/app/panels/graph/styleEditor.html +++ b/src/app/panels/graph/styleEditor.html @@ -27,7 +27,7 @@
- +
diff --git a/src/app/panels/text/editor.html b/src/app/panels/text/editor.html index 6af4dc069c2..b3b8afbbec0 100644 --- a/src/app/panels/text/editor.html +++ b/src/app/panels/text/editor.html @@ -9,10 +9,9 @@
- -
\ No newline at end of file + diff --git a/src/app/partials/annotations_editor.html b/src/app/partials/annotations_editor.html index cb9db2244d6..c72194b6f6a 100644 --- a/src/app/partials/annotations_editor.html +++ b/src/app/partials/annotations_editor.html @@ -16,6 +16,9 @@
+
+ No annotations defined +
diff --git a/src/app/partials/dashboard_topnav.html b/src/app/partials/dashboard_topnav.html index 15ea694058c..b6db19d33aa 100644 --- a/src/app/partials/dashboard_topnav.html +++ b/src/app/partials/dashboard_topnav.html @@ -1,7 +1,10 @@