From b1691f1cd13ccf93bc36199c52fe018c7316ace0 Mon Sep 17 00:00:00 2001 From: Dan Cech Date: Thu, 27 Apr 2017 15:23:49 -0400 Subject: [PATCH 01/85] add support for column aliases in table panel --- public/app/plugins/panel/table/editor.html | 102 ++++++++++++--------- public/app/plugins/panel/table/editor.ts | 1 + public/app/plugins/panel/table/module.html | 2 +- public/app/plugins/panel/table/module.ts | 21 +++++ public/app/plugins/panel/table/renderer.ts | 56 ++++++----- 5 files changed, 114 insertions(+), 68 deletions(-) diff --git a/public/app/plugins/panel/table/editor.html b/public/app/plugins/panel/table/editor.html index 448669ca6a9..561645b7118 100644 --- a/public/app/plugins/panel/table/editor.html +++ b/public/app/plugins/panel/table/editor.html @@ -67,20 +67,38 @@ -
- - +
+ +
-
- + +
+
+ +
+
+ +
+
+ +
+
+
+
+
+ +
+ +
+
@@ -94,41 +112,41 @@
-
+
- -
- -
-
-
-
-
- + +
+ +
+ +
+
+
+ -
-
- - -
-
- - - - - - - - - - -
-
-
- Invert -
-
-
+
+
+ + +
+
+ + + + + + + + + + +
+
+
+ Invert +
+
+
diff --git a/public/app/plugins/panel/table/editor.ts b/public/app/plugins/panel/table/editor.ts index 6b803cc0444..9f36c88c986 100644 --- a/public/app/plugins/panel/table/editor.ts +++ b/public/app/plugins/panel/table/editor.ts @@ -107,6 +107,7 @@ export class TablePanelEditorCtrl { var columnStyleDefaults = { unit: 'short', type: 'number', + alias: '', decimals: 2, colors: ["rgba(245, 54, 54, 0.9)", "rgba(237, 129, 40, 0.89)", "rgba(50, 172, 45, 0.97)"], colorMode: null, diff --git a/public/app/plugins/panel/table/module.html b/public/app/plugins/panel/table/module.html index e405c6c42e3..226fe095ca3 100644 --- a/public/app/plugins/panel/table/module.html +++ b/public/app/plugins/panel/table/module.html @@ -7,7 +7,7 @@
- {{col.text}} + {{col.title || col.text}} diff --git a/public/app/plugins/panel/table/module.ts b/public/app/plugins/panel/table/module.ts index bf872b47d27..155934dc85e 100644 --- a/public/app/plugins/panel/table/module.ts +++ b/public/app/plugins/panel/table/module.ts @@ -9,6 +9,7 @@ import {MetricsPanelCtrl} from 'app/plugins/sdk'; import {transformDataToTable} from './transformers'; import {tablePanelEditor} from './editor'; import {TableRenderer} from './renderer'; +import kbn from 'app/core/utils/kbn'; class TablePanelCtrl extends MetricsPanelCtrl { static templateUrl = 'module.html'; @@ -26,11 +27,13 @@ class TablePanelCtrl extends MetricsPanelCtrl { { type: 'date', pattern: 'Time', + alias: 'Time', dateFormat: 'YYYY-MM-DD HH:mm:ss', }, { unit: 'short', type: 'number', + alias: '', decimals: 2, colors: ["rgba(245, 54, 54, 0.9)", "rgba(237, 129, 40, 0.89)", "rgba(50, 172, 45, 0.97)"], colorMode: null, @@ -118,6 +121,24 @@ class TablePanelCtrl extends MetricsPanelCtrl { render() { this.table = transformDataToTable(this.dataRaw, this.panel); this.table.sort(this.panel.sort); + + for (let colIndex = 0; colIndex < this.table.columns.length; colIndex++) { + let column = this.table.columns[colIndex]; + + for (let i = 0; i < this.panel.styles.length; i++) { + let style = this.panel.styles[i]; + var regex = kbn.stringToJsRegex(style.pattern); + const matches = column.text.match(regex); + if (matches) { + column.style = style; + if (style.alias) { + column.title = column.text.replace(regex, style.alias); + } + break; + } + } + } + return super.render(this.table); } diff --git a/public/app/plugins/panel/table/renderer.ts b/public/app/plugins/panel/table/renderer.ts index 2f5a51ab111..f51af0a0de4 100644 --- a/public/app/plugins/panel/table/renderer.ts +++ b/public/app/plugins/panel/table/renderer.ts @@ -24,7 +24,7 @@ export class TableRenderer { return _.first(style.colors); } - defaultCellFormater(v, style) { + defaultCellFormatter(v, style) { if (v === null || v === void 0 || v === undefined) { return ''; } @@ -40,18 +40,18 @@ export class TableRenderer { } } - createColumnFormater(style, column) { - if (!style) { - return this.defaultCellFormater; + createColumnFormatter(column) { + if (!column.style) { + return this.defaultCellFormatter; } - if (style.type === 'hidden') { + if (column.style.type === 'hidden') { return v => { return undefined; }; } - if (style.type === 'date') { + if (column.style.type === 'date') { return v => { if (v === undefined || v === null) { return '-'; @@ -62,12 +62,12 @@ export class TableRenderer { if (this.isUtc) { date = date.utc(); } - return date.format(style.dateFormat); + return date.format(column.style.dateFormat); }; } - if (style.type === 'number') { - let valueFormater = kbn.valueFormats[column.unit || style.unit]; + if (column.style.type === 'number') { + let valueFormatter = kbn.valueFormats[column.unit || column.style.unit]; return v => { if (v === null || v === void 0) { @@ -75,38 +75,44 @@ export class TableRenderer { } if (_.isString(v)) { - return this.defaultCellFormater(v, style); + return this.defaultCellFormatter(v, column.style); } - if (style.colorMode) { - this.colorState[style.colorMode] = this.getColorForValue(v, style); + if (column.style.colorMode) { + this.colorState[column.style.colorMode] = this.getColorForValue(v, column.style); } - return valueFormater(v, style.decimals, null); + return valueFormatter(v, column.style.decimals, null); }; } return (value) => { - return this.defaultCellFormater(value, style); + return this.defaultCellFormatter(value, column.style); }; } formatColumnValue(colIndex, value) { - if (this.formaters[colIndex]) { - return this.formaters[colIndex](value); - } - - for (let i = 0; i < this.panel.styles.length; i++) { - let style = this.panel.styles[i]; + if (!this.formaters[colIndex]) { let column = this.table.columns[colIndex]; - var regex = kbn.stringToJsRegex(style.pattern); - if (column.text.match(regex)) { - this.formaters[colIndex] = this.createColumnFormater(style, column); - return this.formaters[colIndex](value); + + if (!column.style) { + for (let i = 0; i < this.panel.styles.length; i++) { + let style = this.panel.styles[i]; + var regex = kbn.stringToJsRegex(style.pattern); + const matches = column.text.match(regex); + if (matches) { + column.style = style; + if (style.alias) { + column.title = column.text.replace(regex, style.alias); + } + break; + } + } } + + this.formaters[colIndex] = this.createColumnFormatter(column); } - this.formaters[colIndex] = this.defaultCellFormater; return this.formaters[colIndex](value); } From 2361e2ddd9484e95095fa85775f352d994a2fe62 Mon Sep 17 00:00:00 2001 From: Dan Cech Date: Thu, 27 Apr 2017 15:24:05 -0400 Subject: [PATCH 02/85] fix moment.js deprecation warning when running tests --- public/app/plugins/panel/heatmap/specs/renderer_specs.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/public/app/plugins/panel/heatmap/specs/renderer_specs.ts b/public/app/plugins/panel/heatmap/specs/renderer_specs.ts index 3467c5b7542..3bb3c04d122 100644 --- a/public/app/plugins/panel/heatmap/specs/renderer_specs.ts +++ b/public/app/plugins/panel/heatmap/specs/renderer_specs.ts @@ -81,8 +81,8 @@ describe('grafanaHeatmap', function () { getTimezone: sinon.stub().returns('utc') }, range: { - from: moment.utc("01 Mar 2017 10:00:00"), - to: moment.utc("01 Mar 2017 11:00:00"), + from: moment.utc("01 Mar 2017 10:00:00", 'DD MMM YYYY HH:mm:ss'), + to: moment.utc("01 Mar 2017 11:00:00", 'DD MMM YYYY HH:mm:ss'), }, }; @@ -263,5 +263,5 @@ function getTicks(element, axisSelector) { function formatLocalTime(timeStr) { let format = "HH:mm"; - return moment.utc(timeStr).local().format(format); + return moment.utc(timeStr, 'DD MMM YYYY HH:mm:ss').local().format(format); } From 83c138f5757d09b0df0404f4d5404ba27eedc0c7 Mon Sep 17 00:00:00 2001 From: Daniel Lee Date: Tue, 2 May 2017 09:39:50 +0200 Subject: [PATCH 03/85] docs: fix mistake in api docs --- docs/sources/http_api/admin.md | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/docs/sources/http_api/admin.md b/docs/sources/http_api/admin.md index 950ea4c8202..9c2c6d14b31 100644 --- a/docs/sources/http_api/admin.md +++ b/docs/sources/http_api/admin.md @@ -280,14 +280,22 @@ Change password for specific user ## Pause all alerts -`DELETE /api/admin/pause-all-alerts` +`POST /api/admin/pause-all-alerts` **Example Request**: - DELETE /api/admin/pause-all-alerts HTTP/1.1 + POST /api/admin/pause-all-alerts HTTP/1.1 Accept: application/json Content-Type: application/json + { + "paused": true + } + +JSON Body schema: + +- **paused** – If true then all alerts are to be paused, false unpauses all alerts. + **Example Response**: HTTP/1.1 200 From 0fd96b951ae2b8fda8bedd1df05cf4477f548bf9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Tue, 2 May 2017 09:55:18 +0200 Subject: [PATCH 04/85] wip: heatmap refactor --- .../app/plugins/panel/heatmap/axes_editor.ts | 25 +++++++- .../app/plugins/panel/heatmap/heatmap_ctrl.ts | 8 +-- public/app/plugins/panel/heatmap/module.html | 1 - .../panel/heatmap/partials/axes_editor.html | 58 ++++++------------- 4 files changed, 44 insertions(+), 48 deletions(-) diff --git a/public/app/plugins/panel/heatmap/axes_editor.ts b/public/app/plugins/panel/heatmap/axes_editor.ts index b8fcba871a7..2bf4ac05622 100644 --- a/public/app/plugins/panel/heatmap/axes_editor.ts +++ b/public/app/plugins/panel/heatmap/axes_editor.ts @@ -8,13 +8,14 @@ export class AxesEditorCtrl { unitFormats: any; logScales: any; dataFormats: any; + yBucketOptions: any[]; + xBucketOptions: any[]; /** @ngInject */ - constructor($scope) { + constructor($scope, uiSegmentSrv) { $scope.editor = this; this.panelCtrl = $scope.ctrl; this.panel = this.panelCtrl.panel; - this.unitFormats = kbn.getUnitFormats(); this.logScales = { @@ -29,6 +30,26 @@ export class AxesEditorCtrl { 'Time series': 'timeseries', 'Time series Pre-bucketed': 'tsbuckets' }; + + this.yBucketOptions = [ + {text: '5', value: '5'}, + {text: '10', value: '10'}, + {text: '20', value: '20'}, + {text: '30', value: '30'}, + {text: '50', value: '50'}, + ]; + + this.xBucketOptions = [ + {text: '15', value: '15'}, + {text: '20', value: '20'}, + {text: '30', value: '30'}, + {text: '50', value: '50'}, + {text: '1m', value: '1m'}, + {text: '5m', value: '5m'}, + {text: '10m', value: '10m'}, + {text: '20m', value: '20m'}, + {text: '1h', value: '1h'}, + ]; } setUnitFormat(subItem) { diff --git a/public/app/plugins/panel/heatmap/heatmap_ctrl.ts b/public/app/plugins/panel/heatmap/heatmap_ctrl.ts index c0d3a7bd25d..8a7672f7a23 100644 --- a/public/app/plugins/panel/heatmap/heatmap_ctrl.ts +++ b/public/app/plugins/panel/heatmap/heatmap_ctrl.ts @@ -28,12 +28,9 @@ let panelDefaults = { fillBackground: false }, dataFormat: 'timeseries', - xBucketSize: null, - xBucketNumber: null, - yBucketSize: null, - yBucketNumber: null, xAxis: { - show: true + show: true, + buckets: 'auto', }, yAxis: { show: true, @@ -43,6 +40,7 @@ let panelDefaults = { splitFactor: null, min: null, max: null, + buckets: 'auto', removeZeroValues: false }, tooltip: { diff --git a/public/app/plugins/panel/heatmap/module.html b/public/app/plugins/panel/heatmap/module.html index a59092b2687..6cb89f2e1f2 100644 --- a/public/app/plugins/panel/heatmap/module.html +++ b/public/app/plugins/panel/heatmap/module.html @@ -7,6 +7,5 @@
-
diff --git a/public/app/plugins/panel/heatmap/partials/axes_editor.html b/public/app/plugins/panel/heatmap/partials/axes_editor.html index 161be2dab19..a8dd1a05473 100644 --- a/public/app/plugins/panel/heatmap/partials/axes_editor.html +++ b/public/app/plugins/panel/heatmap/partials/axes_editor.html @@ -1,12 +1,8 @@
Y Axis
- -
- +
- +
-
- - +
+ +
-
- - +
+ +
- +
-
- - -
-
- - +
+
+ + +
- +
- -
X Axis
- -
- +
-
- - -
From ac6c93b3da2392715c56af10644c5a4bc377fa00 Mon Sep 17 00:00:00 2001 From: Daniel Lee Date: Tue, 2 May 2017 10:20:09 +0200 Subject: [PATCH 05/85] docs: add publish bash script --- docs/publish.sh | 3 +++ 1 file changed, 3 insertions(+) create mode 100755 docs/publish.sh diff --git a/docs/publish.sh b/docs/publish.sh new file mode 100755 index 00000000000..4b72f892179 --- /dev/null +++ b/docs/publish.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +make publish ENV=prod VERSION=root From b4cfb225cf6432cb3d3477b158825d1cb697ce60 Mon Sep 17 00:00:00 2001 From: Daniel Lee Date: Tue, 2 May 2017 12:47:32 +0200 Subject: [PATCH 06/85] tech: updates log15 vendor packages Fixes #8262 --- .../inconshreveable/log15/.travis.yml | 10 ---- .../inconshreveable/log15/README.md | 13 +++-- .../github.com/inconshreveable/log15/doc.go | 4 +- .../inconshreveable/log15/format.go | 54 +++++++++++++------ .../inconshreveable/log15/handler.go | 2 +- .../inconshreveable/log15/syslog.go | 2 +- .../log15/term/terminal_darwin.go | 1 + .../log15/term/terminal_netbsd.go | 7 +++ .../log15/term/terminal_notwindows.go | 2 +- .../log15/term/terminal_solaris.go | 9 ++++ vendor/vendor.json | 12 +++++ 11 files changed, 82 insertions(+), 34 deletions(-) delete mode 100644 vendor/github.com/inconshreveable/log15/.travis.yml create mode 100644 vendor/github.com/inconshreveable/log15/term/terminal_netbsd.go create mode 100644 vendor/github.com/inconshreveable/log15/term/terminal_solaris.go diff --git a/vendor/github.com/inconshreveable/log15/.travis.yml b/vendor/github.com/inconshreveable/log15/.travis.yml deleted file mode 100644 index ff5d75e72b9..00000000000 --- a/vendor/github.com/inconshreveable/log15/.travis.yml +++ /dev/null @@ -1,10 +0,0 @@ -language: go - -go: - - 1.1 - - 1.2 - - 1.3 - - 1.4 - - 1.5 - - 1.6 - - tip diff --git a/vendor/github.com/inconshreveable/log15/README.md b/vendor/github.com/inconshreveable/log15/README.md index 8ccd5a38d05..0951b21cb53 100644 --- a/vendor/github.com/inconshreveable/log15/README.md +++ b/vendor/github.com/inconshreveable/log15/README.md @@ -2,7 +2,7 @@ # log15 [![godoc reference](https://godoc.org/github.com/inconshreveable/log15?status.png)](https://godoc.org/github.com/inconshreveable/log15) [![Build Status](https://travis-ci.org/inconshreveable/log15.svg?branch=master)](https://travis-ci.org/inconshreveable/log15) -Package log15 provides an opinionated, simple toolkit for best-practice logging in Go (golang) that is both human and machine readable. It is modeled after the Go standard library's [`io`](http://golang.org/pkg/io/) and [`net/http`](http://golang.org/pkg/net/http/) packages and is an alternative to the standard library's [`log`](http://golang.org/pkg/log/) package. +Package log15 provides an opinionated, simple toolkit for best-practice logging in Go (golang) that is both human and machine readable. It is modeled after the Go standard library's [`io`](http://golang.org/pkg/io/) and [`net/http`](http://golang.org/pkg/net/http/) packages and is an alternative to the standard library's [`log`](http://golang.org/pkg/log/) package. ## Features - A simple, easy-to-understand API @@ -30,7 +30,7 @@ import log "github.com/inconshreveable/log15" // all loggers can have key/value context srvlog := log.New("module", "app/server") -// all log messages can have key/value context +// all log messages can have key/value context srvlog.Warn("abnormal conn rate", "rate", curRate, "low", lowRate, "high", highRate) // child loggers with inherited context @@ -45,7 +45,14 @@ srvlog.SetHandler(log.MultiHandler( log.StreamHandler(os.Stderr, log.LogfmtFormat()), log.LvlFilterHandler( log.LvlError, - log.Must.FileHandler("errors.json", log.JsonFormat()))) + log.Must.FileHandler("errors.json", log.JsonFormat())))) +``` + +Will result in output that looks like this: + +``` +WARN[06-17|21:58:10] abnormal conn rate module=app/server rate=0.500 low=0.100 high=0.800 +INFO[06-17|21:58:10] connection open module=app/server raddr=10.0.0.1 ``` ## Breaking API Changes diff --git a/vendor/github.com/inconshreveable/log15/doc.go b/vendor/github.com/inconshreveable/log15/doc.go index a5cc87419c4..ab7e090d95f 100644 --- a/vendor/github.com/inconshreveable/log15/doc.go +++ b/vendor/github.com/inconshreveable/log15/doc.go @@ -97,7 +97,7 @@ context, CallerFileHandler, CallerFuncHandler and CallerStackHandler. Here's an example that adds the source file and line number of each logging call to the context. - h := log.CallerFileHandler(log.StdoutHandler()) + h := log.CallerFileHandler(log.StdoutHandler) log.Root().SetHandler(h) ... log.Error("open file", "err", err) @@ -108,7 +108,7 @@ This will output a line that looks like: Here's an example that logs the call stack rather than just the call site. - h := log.CallerStackHandler("%+v", log.StdoutHandler()) + h := log.CallerStackHandler("%+v", log.StdoutHandler) log.Root().SetHandler(h) ... log.Error("open file", "err", err) diff --git a/vendor/github.com/inconshreveable/log15/format.go b/vendor/github.com/inconshreveable/log15/format.go index 3468f3048f3..f31b42fcaa3 100644 --- a/vendor/github.com/inconshreveable/log15/format.go +++ b/vendor/github.com/inconshreveable/log15/format.go @@ -7,6 +7,7 @@ import ( "reflect" "strconv" "strings" + "sync" "time" ) @@ -108,7 +109,9 @@ func logfmt(buf *bytes.Buffer, ctx []interface{}, color int) { if color > 0 { fmt.Fprintf(buf, "\x1b[%dm%s\x1b[0m=%s", color, k, v) } else { - fmt.Fprintf(buf, "%s=%s", k, v) + buf.WriteString(k) + buf.WriteByte('=') + buf.WriteString(v) } } @@ -205,6 +208,12 @@ func formatLogfmtValue(value interface{}) string { return "nil" } + if t, ok := value.(time.Time); ok { + // Performance optimization: No need for escaping since the provided + // timeFormat doesn't have any escape characters, and escaping is + // expensive. + return t.Format(timeFormat) + } value = formatShared(value) switch v := value.(type) { case bool: @@ -222,36 +231,49 @@ func formatLogfmtValue(value interface{}) string { } } +var stringBufPool = sync.Pool{ + New: func() interface{} { return new(bytes.Buffer) }, +} + func escapeString(s string) string { - needQuotes := false - e := bytes.Buffer{} - e.WriteByte('"') + needsQuotes := false + needsEscape := false for _, r := range s { if r <= ' ' || r == '=' || r == '"' { - needQuotes = true + needsQuotes = true } - + if r == '\\' || r == '"' || r == '\n' || r == '\r' || r == '\t' { + needsEscape = true + } + } + if needsEscape == false && needsQuotes == false { + return s + } + e := stringBufPool.Get().(*bytes.Buffer) + e.WriteByte('"') + for _, r := range s { switch r { case '\\', '"': e.WriteByte('\\') e.WriteByte(byte(r)) case '\n': - e.WriteByte('\\') - e.WriteByte('n') + e.WriteString("\\n") case '\r': - e.WriteByte('\\') - e.WriteByte('r') + e.WriteString("\\r") case '\t': - e.WriteByte('\\') - e.WriteByte('t') + e.WriteString("\\t") default: e.WriteRune(r) } } e.WriteByte('"') - start, stop := 0, e.Len() - if !needQuotes { - start, stop = 1, stop-1 + var ret string + if needsQuotes { + ret = e.String() + } else { + ret = string(e.Bytes()[1 : e.Len()-1]) } - return string(e.Bytes()[start:stop]) + e.Reset() + stringBufPool.Put(e) + return ret } diff --git a/vendor/github.com/inconshreveable/log15/handler.go b/vendor/github.com/inconshreveable/log15/handler.go index 43205608cc1..fa4570a69ec 100644 --- a/vendor/github.com/inconshreveable/log15/handler.go +++ b/vendor/github.com/inconshreveable/log15/handler.go @@ -180,7 +180,7 @@ func MatchFilterHandler(key string, value interface{}, h Handler) Handler { // level to the wrapped Handler. For example, to only // log Error/Crit records: // -// log.LvlFilterHandler(log.Error, log.StdoutHandler) +// log.LvlFilterHandler(log.LvlError, log.StdoutHandler) // func LvlFilterHandler(maxLvl Lvl, h Handler) Handler { return FilterHandler(func(r *Record) (pass bool) { diff --git a/vendor/github.com/inconshreveable/log15/syslog.go b/vendor/github.com/inconshreveable/log15/syslog.go index 5f95f99f1ee..813481b5669 100644 --- a/vendor/github.com/inconshreveable/log15/syslog.go +++ b/vendor/github.com/inconshreveable/log15/syslog.go @@ -14,7 +14,7 @@ func SyslogHandler(priority syslog.Priority, tag string, fmtr Format) (Handler, return sharedSyslog(fmtr, wr, err) } -// SyslogHandler opens a connection to a log daemon over the network and writes +// SyslogNetHandler opens a connection to a log daemon over the network and writes // all log records to it. func SyslogNetHandler(net, addr string, priority syslog.Priority, tag string, fmtr Format) (Handler, error) { wr, err := syslog.Dial(net, addr, priority, tag) diff --git a/vendor/github.com/inconshreveable/log15/term/terminal_darwin.go b/vendor/github.com/inconshreveable/log15/term/terminal_darwin.go index b05de4cb8c8..d8f351b1b1a 100644 --- a/vendor/github.com/inconshreveable/log15/term/terminal_darwin.go +++ b/vendor/github.com/inconshreveable/log15/term/terminal_darwin.go @@ -2,6 +2,7 @@ // Copyright 2013 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// +build !appengine package term diff --git a/vendor/github.com/inconshreveable/log15/term/terminal_netbsd.go b/vendor/github.com/inconshreveable/log15/term/terminal_netbsd.go new file mode 100644 index 00000000000..f9bb9e1c23b --- /dev/null +++ b/vendor/github.com/inconshreveable/log15/term/terminal_netbsd.go @@ -0,0 +1,7 @@ +package term + +import "syscall" + +const ioctlReadTermios = syscall.TIOCGETA + +type Termios syscall.Termios diff --git a/vendor/github.com/inconshreveable/log15/term/terminal_notwindows.go b/vendor/github.com/inconshreveable/log15/term/terminal_notwindows.go index 87df7d5b029..c9af534f62f 100644 --- a/vendor/github.com/inconshreveable/log15/term/terminal_notwindows.go +++ b/vendor/github.com/inconshreveable/log15/term/terminal_notwindows.go @@ -3,7 +3,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build linux,!appengine darwin freebsd openbsd +// +build linux,!appengine darwin freebsd openbsd netbsd package term diff --git a/vendor/github.com/inconshreveable/log15/term/terminal_solaris.go b/vendor/github.com/inconshreveable/log15/term/terminal_solaris.go new file mode 100644 index 00000000000..033c163246e --- /dev/null +++ b/vendor/github.com/inconshreveable/log15/term/terminal_solaris.go @@ -0,0 +1,9 @@ +package term + +import "golang.org/x/sys/unix" + +// IsTty returns true if the given file descriptor is a terminal. +func IsTty(fd uintptr) bool { + _, err := unix.IoctlGetTermios(int(fd), unix.TCGETA) + return err == nil +} diff --git a/vendor/vendor.json b/vendor/vendor.json index 4e884428eb7..9d80bd38f84 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -416,6 +416,18 @@ "revision": "3ab3a8b8831546bd18fd182c20687ca853b2bb13", "revisionTime": "2016-12-15T22:53:35Z" }, + { + "checksumSHA1": "mrmfY0cVu7jvgoIuTRaR8yVVh/M=", + "path": "github.com/inconshreveable/log15", + "revision": "39bacc234bf1afd0b68573e95b45871f67ba2cd4", + "revisionTime": "2017-02-16T22:56:31Z" + }, + { + "checksumSHA1": "oVIIInZXKkcRozJfuH2vWJsAS7s=", + "path": "github.com/inconshreveable/log15/term", + "revision": "39bacc234bf1afd0b68573e95b45871f67ba2cd4", + "revisionTime": "2017-02-16T22:56:31Z" + }, { "checksumSHA1": "BM6ZlNJmtKy3GBoWwg2X55gnZ4A=", "path": "github.com/klauspost/crc32", From 665cf55e6e0b1d332377654990e98d2fcee6ceb2 Mon Sep 17 00:00:00 2001 From: Dan Cech Date: Tue, 2 May 2017 08:37:56 -0400 Subject: [PATCH 07/85] make generic oauth provider flexible enough to handle bitbucket's oauth implementation (#8248) --- pkg/social/generic_oauth.go | 44 ++++++++++++++++++++++++++++--------- 1 file changed, 34 insertions(+), 10 deletions(-) diff --git a/pkg/social/generic_oauth.go b/pkg/social/generic_oauth.go index f0e36ac064c..04b0536852a 100644 --- a/pkg/social/generic_oauth.go +++ b/pkg/social/generic_oauth.go @@ -4,6 +4,7 @@ import ( "encoding/json" "errors" "fmt" + "io/ioutil" "net/http" "github.com/grafana/grafana/pkg/models" @@ -76,9 +77,11 @@ func (s *GenericOAuth) IsOrganizationMember(client *http.Client) bool { func (s *GenericOAuth) FetchPrivateEmail(client *http.Client) (string, error) { type Record struct { - Email string `json:"email"` - Primary bool `json:"primary"` - Verified bool `json:"verified"` + Email string `json:"email"` + Primary bool `json:"primary"` + IsPrimary bool `json:"is_primary"` + Verified bool `json:"verified"` + IsConfirmed bool `json:"is_confirmed"` } emailsUrl := fmt.Sprintf(s.apiUrl + "/emails") @@ -91,14 +94,30 @@ func (s *GenericOAuth) FetchPrivateEmail(client *http.Client) (string, error) { var records []Record - if err = json.NewDecoder(r.Body).Decode(&records); err != nil { + body, err := ioutil.ReadAll(r.Body) + if err != nil { return "", err } + err = json.Unmarshal(body, records) + if err != nil { + var data struct { + Values []Record `json:"values"` + } + + err = json.Unmarshal(body, &data) + if err != nil { + return "", err + } + + records = data.Values + } + var email = "" for _, record := range records { - if record.Primary { + if record.Primary || record.IsPrimary { email = record.Email + break } } @@ -161,11 +180,12 @@ func (s *GenericOAuth) FetchOrganizations(client *http.Client) ([]string, error) func (s *GenericOAuth) UserInfo(client *http.Client) (*BasicUserInfo, error) { var data struct { - Name string `json:"name"` - Login string `json:"login"` - Username string `json:"username"` - Email string `json:"email"` - Attributes map[string][]string `json:"attributes"` + Name string `json:"name"` + DisplayName string `json:"display_name"` + Login string `json:"login"` + Username string `json:"username"` + Email string `json:"email"` + Attributes map[string][]string `json:"attributes"` } var err error @@ -197,6 +217,10 @@ func (s *GenericOAuth) UserInfo(client *http.Client) (*BasicUserInfo, error) { } } + if userInfo.Name == "" && data.DisplayName != "" { + userInfo.Name = data.DisplayName + } + if userInfo.Login == "" && data.Username != "" { userInfo.Login = data.Username } From ad3da0f47c43b90cc9d330c2b679de1ad4f6f310 Mon Sep 17 00:00:00 2001 From: Andrei Stefan Date: Tue, 2 May 2017 15:50:10 +0300 Subject: [PATCH 08/85] add icon guide --- .../app/features/styleguide/styleguide.html | 6 +++ public/app/features/styleguide/styleguide.ts | 13 ++++- public/sass/icons.json | 52 +++++++++++++++++++ public/sass/pages/_styleguide.scss | 5 ++ 4 files changed, 75 insertions(+), 1 deletion(-) create mode 100644 public/sass/icons.json diff --git a/public/app/features/styleguide/styleguide.html b/public/app/features/styleguide/styleguide.html index 2f42ca7e671..4bb0e8ac5a9 100644 --- a/public/app/features/styleguide/styleguide.html +++ b/public/app/features/styleguide/styleguide.html @@ -43,6 +43,12 @@
+
+
+ +
+
+
forms
diff --git a/public/app/features/styleguide/styleguide.ts b/public/app/features/styleguide/styleguide.ts index c297f12ab4c..dc5c5d94072 100644 --- a/public/app/features/styleguide/styleguide.ts +++ b/public/app/features/styleguide/styleguide.ts @@ -9,8 +9,9 @@ class StyleGuideCtrl { buttonNames = ['primary', 'secondary', 'inverse', 'success', 'warning', 'danger']; buttonSizes = ['btn-small', '', 'btn-large']; buttonVariants = ['-', '-outline-']; + icons: any = []; page: any; - pages = ['colors', 'buttons']; + pages = ['colors', 'buttons', 'icons']; /** @ngInject **/ constructor(private $http, private $routeParams, private $location) { @@ -26,6 +27,10 @@ class StyleGuideCtrl { if (this.page.colors) { this.loadColors(); } + + if (this.page.icons) { + this.loadIcons(); + } } loadColors() { @@ -36,6 +41,12 @@ class StyleGuideCtrl { }); } + loadIcons() { + this.$http.get('public/sass/icons.json').then(res => { + this.icons = res.data; + }); + } + switchTheme() { this.$routeParams.theme = this.theme === 'dark' ? 'light' : 'dark'; this.$location.search(this.$routeParams); diff --git a/public/sass/icons.json b/public/sass/icons.json new file mode 100644 index 00000000000..593c7bbfb55 --- /dev/null +++ b/public/sass/icons.json @@ -0,0 +1,52 @@ +[ + "raintank_wordmark", + "raintank_r-icn", + "check-alt", + "check", + "collector", + "dashboard", + "panel", + "datasources", + "endpoint-tiny", + "endpoint", + "page", + "filter", + "status", + "monitoring", + "monitoring-tiny", + "jump-to-dashboard", + "warning", + "nodata", + "critical", + "crit", + "online", + "event-error", + "event", + "sadface", + "private-collector", + "alert-disabled", + "refresh", + "save", + "share", + "star", + "search", + "remove", + "video", + "bulk_action", + "grabber", + "users", + "globe", + "snapshot", + "play-grafana-icon", + "grafana-icon", + "email", + "stopwatch", + "skull", + "probe", + "apps", + "scale", + "pending", + "verified", + "worldping", + "grafana_wordmark" +] \ No newline at end of file diff --git a/public/sass/pages/_styleguide.scss b/public/sass/pages/_styleguide.scss index 55aabb01f01..70a71cda1be 100644 --- a/public/sass/pages/_styleguide.scss +++ b/public/sass/pages/_styleguide.scss @@ -21,6 +21,11 @@ } } +.style-guide-icon-list { + font-size: 2em; + text-align: center; +} + // define("areas/styleguide/static/script/app/colors", [], function() { // "use strict"; // var a = function(a) { From f43c749422f546580abe8317fa569cc95b65fe4a Mon Sep 17 00:00:00 2001 From: Andrei Stefan Date: Tue, 2 May 2017 16:15:28 +0300 Subject: [PATCH 09/85] add plugin authoring tab in style guide --- public/app/features/styleguide/styleguide.html | 5 +++++ public/app/features/styleguide/styleguide.ts | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/public/app/features/styleguide/styleguide.html b/public/app/features/styleguide/styleguide.html index 4bb0e8ac5a9..e573a0e3b09 100644 --- a/public/app/features/styleguide/styleguide.html +++ b/public/app/features/styleguide/styleguide.html @@ -49,6 +49,11 @@
+
+

From grafana 3.0 it's very easy to develop your own plugins and share them with other grafana users.

+

More information about plugin development can be found at docs.grafana.org

+
+
forms
diff --git a/public/app/features/styleguide/styleguide.ts b/public/app/features/styleguide/styleguide.ts index dc5c5d94072..0397b7254a3 100644 --- a/public/app/features/styleguide/styleguide.ts +++ b/public/app/features/styleguide/styleguide.ts @@ -11,7 +11,7 @@ class StyleGuideCtrl { buttonVariants = ['-', '-outline-']; icons: any = []; page: any; - pages = ['colors', 'buttons', 'icons']; + pages = ['colors', 'buttons', 'icons', 'plugins']; /** @ngInject **/ constructor(private $http, private $routeParams, private $location) { From 53ccc6f48fe612879a2b414eacc406e655abff3a Mon Sep 17 00:00:00 2001 From: Andrei Stefan Date: Tue, 2 May 2017 16:45:34 +0300 Subject: [PATCH 10/85] fix theme switching in style guide --- public/app/features/styleguide/styleguide.html | 4 ---- public/app/features/styleguide/styleguide.ts | 10 +++++++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/public/app/features/styleguide/styleguide.html b/public/app/features/styleguide/styleguide.html index e573a0e3b09..e2a218bb763 100644 --- a/public/app/features/styleguide/styleguide.html +++ b/public/app/features/styleguide/styleguide.html @@ -9,10 +9,6 @@ Switch theme - - - Reload -
    diff --git a/public/app/features/styleguide/styleguide.ts b/public/app/features/styleguide/styleguide.ts index 0397b7254a3..de9da6aafd7 100644 --- a/public/app/features/styleguide/styleguide.ts +++ b/public/app/features/styleguide/styleguide.ts @@ -14,7 +14,7 @@ class StyleGuideCtrl { pages = ['colors', 'buttons', 'icons', 'plugins']; /** @ngInject **/ - constructor(private $http, private $routeParams, private $location) { + constructor(private $http, private $routeParams, private $location, private backendSrv) { this.theme = config.bootData.user.lightTheme ? 'light': 'dark'; this.page = {}; @@ -49,8 +49,12 @@ class StyleGuideCtrl { switchTheme() { this.$routeParams.theme = this.theme === 'dark' ? 'light' : 'dark'; - this.$location.search(this.$routeParams); - setTimeout(() => { + + var cmd = { + theme: this.$routeParams.theme + }; + + this.backendSrv.put('/api/user/preferences', cmd).then(() => { window.location.href = window.location.href; }); } From c1de972eb5fa337b98a8886431f85e74e98a64ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Tue, 2 May 2017 16:22:31 +0200 Subject: [PATCH 11/85] docs: wip templating docs --- docs/sources/reference/templating.md | 36 ++++++++++++++++++++++++---- 1 file changed, 31 insertions(+), 5 deletions(-) diff --git a/docs/sources/reference/templating.md b/docs/sources/reference/templating.md index 636cc62a0c8..422a655d5f2 100644 --- a/docs/sources/reference/templating.md +++ b/docs/sources/reference/templating.md @@ -10,19 +10,36 @@ weight = 1 # Templating - + -Dashboard Templating allows you to make your Dashboards more interactive and dynamic. +Templating allows you to make your dashboards more interactive and dynamic. They’re one of the more powerful and complex features in Grafana. The templating +feature allows you to create variables that are shown as dropdown select boxes at the top of the dashboard. +These dropdowns makes it easy to change the variable value and in turn quickly change the data being displayed. -They’re one of the most powerful and most used features of Grafana, and they’ve recently gotten even more attention in Grafana 2.0 and Grafana 2.1. +## What is a variable? -You can create Dashboard Template variables that can be used practically anywhere in a Dashboard: data queries on individual Panels (within the Query Editor), the names in your legends, or titles in Panels and Rows. +A variable is a placeholder for a value. You can use variables in metric queries and in panel titles. So when you change +the value, using the dropdown at the top of the dashboard, your panel's metric queries will change to reflect the new value. -You can configure Dashboard Templating by clicking the dropdown cog on the top of the Dashboard while viewing it. +### Interpolation +Panel titles and metric queries can refer to variables using two different syntaxes: + +- `$` Example: apps.frontend.$server.requests.count +- `[[varname]]` Example: apps.frontend.[[server]].requests.count + +Why two ways? The first syntax is easier to read and write but does not allow you to use a variable in the middle of word. Use +the second syntax for scenarios like this: `my.server[[serverNumber]].count`. + +Before queries are sent to your data source the query is **interpolated**, meaning the variable is replaced with its current value. During +interpolation the variable value might be **escaped** in order to conform to the syntax of the query langauge of where it is used. For example +a variable used in a regex expression in an InfluxDB or Prometheus query will be regex escaped. Read the data source specific documentation +article for details on value escaping during interpolation. ## Variable types + + There are three different types of Template variables: query, custom, and interval. They can all be used to create dynamic variables that you can use throughout the Dashboard, but they differ in how they get the data for their values. @@ -40,6 +57,15 @@ You can even create nested variables that use other variables in their definitio You can utilize the special ** All ** value to allow the Dashboard user to query for every single Query variable returned. Grafana will automatically translate ** All ** into the appropriate format for your Data Source. +### Annotation query details + +The annotation query options are different for each data source. + +- [Graphite annotation queries]({{< relref "features/datasources/graphite.md#annotations" >}}) +- [Elasticsearch annotation queries]({{< relref "features/datasources/elasticsearch.md#annotations" >}}) +- [InfluxDB annotation queries]({{< relref "features/datasources/influxdb.md#annotations" >}}) +- [Prometheus annotation queries]({{< relref "features/datasources/prometheus.md#annotations" >}}) + #### Multi-select As of Grafana 2.1, it is now possible to select a subset of Query Template variables (previously it was possible to select an individual value or 'All', not multiple values that were less than All). This is accomplished via the Multi-Select option. If enabled, the Dashboard user will be able to enable and disable individual variables. From 6ebb31bed4ada6cae569e02f4f528994ab107698 Mon Sep 17 00:00:00 2001 From: Jon Freedman Date: Tue, 2 May 2017 15:45:48 +0100 Subject: [PATCH 12/85] chore: add comment to clarify that org_id usage As described here: https://community.grafana.com/t/many-to-many-group-dn-org-role-mapping-in-ldap-config/729/2 org_id can be used to allow multiple group_dn's to map to the same org_role provided the org_id differs. --- docs/sources/installation/ldap.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/sources/installation/ldap.md b/docs/sources/installation/ldap.md index e8e1c8e57bd..769ca3fd1ba 100644 --- a/docs/sources/installation/ldap.md +++ b/docs/sources/installation/ldap.md @@ -73,7 +73,7 @@ email = "email" [[servers.group_mappings]] group_dn = "cn=admins,dc=grafana,dc=org" org_role = "Admin" -# The Grafana organization database id, optional, if left out the default org (id 1) will be used +# The Grafana organization database id, optional, if left out the default org (id 1) will be used. Setting this allows for multiple group_dn's to be assigned to the same org_role provided the org_id differs # org_id = 1 [[servers.group_mappings]] From deab4a378a1ca35b53e4e09ece1dde51981a4894 Mon Sep 17 00:00:00 2001 From: Vladimir Gordiychuk Date: Wed, 3 May 2017 09:22:30 +0300 Subject: [PATCH 13/85] bug: MetricSegment lost information about type (#8278) Fixed #8277 --- public/app/core/services/segment_srv.js | 1 + 1 file changed, 1 insertion(+) diff --git a/public/app/core/services/segment_srv.js b/public/app/core/services/segment_srv.js index 096697052ce..f1733fcb3e6 100644 --- a/public/app/core/services/segment_srv.js +++ b/public/app/core/services/segment_srv.js @@ -13,6 +13,7 @@ function (angular, _, coreModule) { if (options === '*' || options.value === '*') { this.value = '*'; this.html = $sce.trustAsHtml(''); + this.type = options.type; this.expandable = true; return; } From b042c5398063e6e6022c4ee993fcfdfbbfe16988 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Wed, 3 May 2017 08:56:51 +0200 Subject: [PATCH 14/85] ux: updated heatmap icon --- pkg/api/frontendsettings.go | 6 +- .../panel/heatmap/img/icn-heatmap-panel.svg | 335 ++++++++---------- 2 files changed, 144 insertions(+), 197 deletions(-) diff --git a/pkg/api/frontendsettings.go b/pkg/api/frontendsettings.go index f7e974db17a..4d84480a208 100644 --- a/pkg/api/frontendsettings.go +++ b/pkg/api/frontendsettings.go @@ -166,10 +166,12 @@ func getPanelSort(id string) int { sort = 3 case "text": sort = 4 - case "alertlist": + case "heatmap": sort = 5 - case "dashlist": + case "alertlist": sort = 6 + case "dashlist": + sort = 7 } return sort } diff --git a/public/app/plugins/panel/heatmap/img/icn-heatmap-panel.svg b/public/app/plugins/panel/heatmap/img/icn-heatmap-panel.svg index 29838f76ca6..932d226b99a 100644 --- a/public/app/plugins/panel/heatmap/img/icn-heatmap-panel.svg +++ b/public/app/plugins/panel/heatmap/img/icn-heatmap-panel.svg @@ -1,195 +1,140 @@ - - - -image/svg+xml \ No newline at end of file + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From c57ec23a668dd2371181dfdf16b82c983a52425c Mon Sep 17 00:00:00 2001 From: Andrei Stefan Date: Wed, 3 May 2017 11:46:08 +0300 Subject: [PATCH 15/85] fix broken layout for styleguide icons tab --- public/app/features/styleguide/styleguide.html | 6 ++++-- public/sass/icons.json | 6 +++--- public/sass/pages/_styleguide.scss | 2 +- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/public/app/features/styleguide/styleguide.html b/public/app/features/styleguide/styleguide.html index e2a218bb763..573d720831e 100644 --- a/public/app/features/styleguide/styleguide.html +++ b/public/app/features/styleguide/styleguide.html @@ -40,8 +40,10 @@
-
- +
+
+ +
diff --git a/public/sass/icons.json b/public/sass/icons.json index 593c7bbfb55..4c87042fe02 100644 --- a/public/sass/icons.json +++ b/public/sass/icons.json @@ -1,4 +1,6 @@ [ + "grafana_wordmark", + "worldping", "raintank_wordmark", "raintank_r-icn", "check-alt", @@ -46,7 +48,5 @@ "apps", "scale", "pending", - "verified", - "worldping", - "grafana_wordmark" + "verified" ] \ No newline at end of file diff --git a/public/sass/pages/_styleguide.scss b/public/sass/pages/_styleguide.scss index 70a71cda1be..66543847574 100644 --- a/public/sass/pages/_styleguide.scss +++ b/public/sass/pages/_styleguide.scss @@ -22,7 +22,7 @@ } .style-guide-icon-list { - font-size: 2em; + font-size: 1.8em; text-align: center; } From 2304a710bf3b68036676172ea280bc57391ce729 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Wed, 3 May 2017 14:09:04 +0200 Subject: [PATCH 16/85] docs: templating doc wip --- docs/sources/reference/templating.md | 142 +++++++++++++++++++-------- 1 file changed, 99 insertions(+), 43 deletions(-) diff --git a/docs/sources/reference/templating.md b/docs/sources/reference/templating.md index 422a655d5f2..5322b8150b3 100644 --- a/docs/sources/reference/templating.md +++ b/docs/sources/reference/templating.md @@ -12,9 +12,9 @@ weight = 1 -Templating allows you to make your dashboards more interactive and dynamic. They’re one of the more powerful and complex features in Grafana. The templating -feature allows you to create variables that are shown as dropdown select boxes at the top of the dashboard. -These dropdowns makes it easy to change the variable value and in turn quickly change the data being displayed. +Templating allows you to make your dashboards more interactive and dynamic. Instead of hard-coding things like server, application +or sensor id in you metric queries you can use variables in their place. Variables are shown as dropdown select boxes at the top of +the dashboard. These dropdowns makes it easy to change the data being displayed in your dashboard. ## What is a variable? @@ -32,80 +32,136 @@ Why two ways? The first syntax is easier to read and write but does not allow yo the second syntax for scenarios like this: `my.server[[serverNumber]].count`. Before queries are sent to your data source the query is **interpolated**, meaning the variable is replaced with its current value. During -interpolation the variable value might be **escaped** in order to conform to the syntax of the query langauge of where it is used. For example -a variable used in a regex expression in an InfluxDB or Prometheus query will be regex escaped. Read the data source specific documentation -article for details on value escaping during interpolation. +interpolation the variable value might be **escaped** in order to conform to the syntax of the query language and where it is used. +For example a variable used in a regex expression in an InfluxDB or Prometheus query will be regex escaped. Read the data source specific +documentation article for details on value escaping during interpolation. -## Variable types +### Variable options + +A variable is presented as a dropdown select box at the top of the dashboards. It has a current value and a set of **options**. The **options** +is the the set of values you can choose from. + +## Adding a variable -There are three different types of Template variables: query, custom, and interval. +You add variables via Dashboard cogs menu > Templating. This opens up a list of variables and a `New` button to create a new variable. -They can all be used to create dynamic variables that you can use throughout the Dashboard, but they differ in how they get the data for their values. +### Basic variable options + +Option | Description +------- | -------- +*Name* | The name of the variable, this is the name you use when you refer to your variable in your metric queries. Must be unique and contain no white-spaces. +*Label* | The name of the dropdown for this variable. +*Hide* | Options to hide the dropdown select box. +*Type* | Defines the variable type. -### Query +### Variable types - > Note: The Query type is Data Source specific. Please consult the appropriate documentation for your particular Data Source. +Type | Description +------- | -------- +*Query* | This variable type allows you to write a data source query that usually returns a list of metric names, tag values or keys. For example a query that returns a list of server names, sensor ids or data centers. +*Interval* | This variable can represent timespans. Instead of hard-coding a group by time or date histogram interval use a variable of this type. +*Datasource* | This type allows you to quickly change data source for an entire Dashboard. Useful if you have multiple instances of a data source in for example different environments. +*Custom* | Define the variable options manually using a comma seperated list. +*Constant* | Define a hidden constant. Useful for metric path prefixes for dashboards you want to share. During dashboard export constant variables will be made into an import option. +*Ad hoc filters* | Very special kind of variable that only works with some data sources, InfluxDB & Elasticsearch currently. It allows you to add key/value filters that will automatically be added to all metric queries that use the specified data source. -Query is the most common type of Template variable. Use the `Query` template type to generate a dynamic list of variables, simply by allowing Grafana to explore your Data Source metric namespace when the Dashboard loads. +### Query options -For example a query like `prod.servers.*` will fill the variable with all possible values that exists in that wildcard position (in the case of the Graphite Data Source). +This variable type is the most powerful and complex as it can dynamically fetch its options using a data source query. -You can even create nested variables that use other variables in their definition. For example `apps.$app.servers.*` uses the variable $app in its own query definition. +Option | Description +------- | -------- +*Data source* | The data source target for the query. +*Refresh* | Controls when to update the variable option list (values in the dropdown). **On Dashboard Load** will slow down dashboard load as the variable query needs to be completed before dashboard can be initialized. Set this only to **On Time Range Change** if your variable options query contains a time range filter or is dependant on dashboard time range. +*Query* | The data source specific query expression. +*Regex* | Regex to filter or capture specific parts of the names return by your data source query. Optional. +*Sort* | Define sort order for options in dropdown. **Disabled** means that the order of options returned by your data source query will be used. -You can utilize the special ** All ** value to allow the Dashboard user to query for every single Query variable returned. Grafana will automatically translate ** All ** into the appropriate format for your Data Source. +### Query expressions -### Annotation query details +The query expressions are different for each data source. -The annotation query options are different for each data source. +- [Graphite templating queries]({{< relref "features/datasources/graphite.md#templating" >}}) +- [Elasticsearch templating queries]({{< relref "features/datasources/elasticsearch.md#templating" >}}) +- [InfluxDB templating queries]({{< relref "features/datasources/influxdb.md#templating" >}}) +- [Prometheus templating queries]({{< relref "features/datasources/prometheus.md#templating" >}}) +- [OpenTSDB templating queries]({{< relref "features/datasources/prometheus.md#templating" >}}) -- [Graphite annotation queries]({{< relref "features/datasources/graphite.md#annotations" >}}) -- [Elasticsearch annotation queries]({{< relref "features/datasources/elasticsearch.md#annotations" >}}) -- [InfluxDB annotation queries]({{< relref "features/datasources/influxdb.md#annotations" >}}) -- [Prometheus annotation queries]({{< relref "features/datasources/prometheus.md#annotations" >}}) +One thing to note is that query expressions can contain references to other variables and in effect create depend & nested +variables. Grafana will detect this and automatically refresh a variable when one of it's containing variables change. -#### Multi-select -As of Grafana 2.1, it is now possible to select a subset of Query Template variables (previously it was possible to select an individual value or 'All', not multiple values that were less than All). This is accomplished via the Multi-Select option. If enabled, the Dashboard user will be able to enable and disable individual variables. +## Selection Options -The Multi-Select functionality is taken a step further with the introduction of Multi-Select Tagging. This functionality allows you to group individual Template variables together under a Tag or Group name. +Option | Description +------- | -------- +*Mulit-value* | If enabled, the variable will support the selection of multiple options at the same time. +*Include All option* | Add a special `All` option whose value includes all options. +*Custom all value* | By default the `All` value will include all options in combined expression. This can become very long and can have performance problems. Many times it can be better to specify a custom all value, like a wildcard regex. To make it possible to have custom regex, globs or lucene syntax in the **Custom all value** option it is never escaped so you will have to think avbout what is a valid value for your data source. -For example, if you were using Templating to list all 20 of your applications, you could use Multi-Select Tagging to group your applications by function or region or criticality, etc. +### Formating multiple values - > Note: Multi-Select Tagging functionality is currently experimental but is part of Grafana 2.1. To enable this feature click the enable icon when editing Template options for a particular variable. +Interpolating a variable with multiple values selected is tricky as it is not straight forward how to format the multiple values to into a string that +is valid in the given context where the variable is used. Grafana tries to solve this by allowing each data source plugin to +inform the templating interpolation engine what format to use for multiple values. - +**Graphite** for example uses glob expressions. A variable with multiple values would in this case be interpolated as `{host1,host2,host3}` if +the current variable value was *host1*, *host2* and *host3*. -Grafana gets the list of tags and the list of values in each tag by performing two queries on your metric namespace. +**InfluxDB and Prometheus** uses regex expressions, so the same variable +would be interpolated as `(host1|host2|host3)`. Every value would also be regex escaped, if not, a value with a regex control character would +break the regex expression. -The Tags query returns a list of Tags. +**Elasticsearch** uses lucene query syntax, so the same variable would in this case be formated as `("host1" OR "host2" OR "host3")`. In this case every value +needs to be escaped so that the value can contain lucene control words and quotation marks. -The Tag values query returns the values for a given Tag. +#### Formating troubles -Note: a proof of concept shim that translates the metric query into a SQL call is provided. This allows you to maintain your tag:value mapping independently of your Data Source. +Automatic escaping & formating can cause problems and it can be tricky to grasp the logic is behind it. +Especially for InfluxDB and Prometheus where the use of regex syntax requires that the variable is used in regex operator context. +If you do not want Grafana to do this automatic regex escaping and formating your only option is to disable the *Multi-value* or *Include All option* +options. -Once configured, Multi-Select Tagging provides a convenient way to group and your template variables, and slice your data in the exact way you want. The Tags can be seen on the right side of the template pull-down. +### Value groups/tags -![](/img/docs/v2/multi-select.gif) +If you have a lot of options in the dropdown for a multi-value variable. You can use this feature to group the values into selectable tags. -### Interval +Option | Description +------- | -------- +*Tags query* | Data source query that should return a list of tags +*Tag values query* | Data source query that should return a list of values for a specified tag key. Use `$tag` in the query to refer the currently selected tag. -Use the `Interval` type to create Template variables around time ranges (eg. `1m`,`1h`, `1d`). There is also a special `auto` option that will change depending on the current time range, you can specify how many times the current time range should be divided to calculate the current `auto` range. +![](/img/docs/v4/variable_dropdown_tags.png) -![](/img/docs/v2/templated_variable_parameter.png) +### Interval variables -### Custom +Use the `Interval` type to create a variable that represent a timespan (eg. `1m`,`1h`, `1d`). There is also a special `auto` option that will change depending on the current time range. You can specify how many times the current time range +should be divided to calculate the current `auto` timespan. -Use the `Custom` type to manually create Template variables around explicit values that are hard-coded into the Dashboard, and not dependent on any Data Source. You can specify multiple Custom Template values by separating them with a comma. +This variable type is useful as parameter to group by time (for InfluxDB), Date histogram interval (for Elasticsearch) or as a *summarize* function parameter (for Graphite). -## Repeating Panels and Repeating Rows +## Repeating Panels -Template Variables can be very useful to dynamically change what you're visualizing on a given panel. Sometimes, you might want to create entire new Panels (or Rows) based on what Template Variables have been selected. This is now possible in Grafana 2.1. +Template variables can be very useful to dynamically change your queries across a whole dashboard. If you want +Grafana to dynamically create new panels or rows based on what values you have selected you can use the *Repeat* feature. -Once you've got your Template variables (of any type) configured the way you'd like, check out the Repeating Panels and Repeating Row documentation +If you have a variable with `Multi-value` or `Include all value` options enabled you can choose one panel or one row and have Grafana repeat that row +for every selected value. You find this option under the General tab in panel edit mode. You select the variable to repeat by, and a `min span`. +The `min span` controls how small Grafana will make the panels (if you have many values selected). Grafana will automatically adjust the width of +each repeated panel so that the whole row is filled. Currently you cannot mix other panels on a row with a repeated panel. -## Screencast - Templated Graphite Queries +Only make changes to the first panel (the original template). To have the changes take effect on all panels you need to trigger a dynamic dashboard re-build. +You can do this by either changing the variable value (that is the basis for the repeat) or reload the dashboard. + +## Repeating Rows + +This option requires you to open the row options view. Hover over the row left side to trigger the row menu, in this menu click `Row Options`. This +opens the row options view. Here you find a *Repeat* dropdown where you can select the variable to repeat by. + +### URL state + +Variable values are always synced to the URL using the syntax `var-=value`. - From e6c29391c641fd8ffad2c72da0c3b05083b49c4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Wed, 3 May 2017 14:20:59 +0200 Subject: [PATCH 17/85] docs: improved Graphite templating docs --- docs/sources/features/datasources/graphite.md | 26 ++++++++++++++++--- docs/sources/reference/templating.md | 6 ++--- 2 files changed, 25 insertions(+), 7 deletions(-) diff --git a/docs/sources/features/datasources/graphite.md b/docs/sources/features/datasources/graphite.md index 58965f8bf6f..34fb48618f7 100644 --- a/docs/sources/features/datasources/graphite.md +++ b/docs/sources/features/datasources/graphite.md @@ -76,17 +76,35 @@ this consolidation is done using `avg` function. You can how Graphite consolidat > client side by Grafana. And depending on your consolidation function only one or two can be correct at the same time. ## Templating -You can create a template variable in Grafana and have that variable filled with values from any Graphite metric exploration query. -You can then use this variable in your Graphite queries, either as part of a metric path or as arguments to functions. -For example a query like `prod.servers.*` will fill the variable with all possible -values that exists in the wildcard position. +Instead of hard-coding things like server, application and sensor name in you metric queries you can use variables in their place. +Variables are shown as dropdown select boxes at the top of the dashboard. These dropdowns makes it easy to change the data +being displayed in your dashboard. + +Checkout the [Templating]({{< relref "reference/templating.md" >}}) documentation for an introduction to the templating feature and the different +types of template variables. + +### Query variables + +The query you specify in the query field should be a metric find type of query. For example a query like `prod.servers.*` will fill the +variable with all possible values that exists in the wildcard position. You can also create nested variables that use other variables in their definition. For example `apps.$app.servers.*` uses the variable `$app` in its query definition. +### Variable usage + +You can use a variable in a metric node path or as a parameter to a function. ![](/img/docs/v2/templated_variable_parameter.png) +There are two syntaxes: + +- `$` Example: apps.frontend.$server.requests.count +- `[[varname]]` Example: apps.frontend.[[server]].requests.count + +Why two ways? The first syntax is easier to read and write but does not allow you to use a variable in the middle of word. Use +the second syntax in expressions like `my.server[[serverNumber]].count`. + ## Annotations [Annotations]({{< relref "reference/annotations.md" >}}) allows you to overlay rich event information on top of graphs. You add annotation diff --git a/docs/sources/reference/templating.md b/docs/sources/reference/templating.md index 5322b8150b3..f16e03c898b 100644 --- a/docs/sources/reference/templating.md +++ b/docs/sources/reference/templating.md @@ -12,8 +12,8 @@ weight = 1 -Templating allows you to make your dashboards more interactive and dynamic. Instead of hard-coding things like server, application -or sensor id in you metric queries you can use variables in their place. Variables are shown as dropdown select boxes at the top of +Templating allows for more interactive and dynamic dashboards. Instead of hard-coding things like server, application +and sensor name in you metric queries you can use variables in their place. Variables are shown as dropdown select boxes at the top of the dashboard. These dropdowns makes it easy to change the data being displayed in your dashboard. ## What is a variable? @@ -29,7 +29,7 @@ Panel titles and metric queries can refer to variables using two different synta - `[[varname]]` Example: apps.frontend.[[server]].requests.count Why two ways? The first syntax is easier to read and write but does not allow you to use a variable in the middle of word. Use -the second syntax for scenarios like this: `my.server[[serverNumber]].count`. +the second syntax in expressions like `my.server[[serverNumber]].count`. Before queries are sent to your data source the query is **interpolated**, meaning the variable is replaced with its current value. During interpolation the variable value might be **escaped** in order to conform to the syntax of the query language and where it is used. From ed8d284715c47069d0533a3d3ede52441be87f34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Wed, 3 May 2017 15:50:38 +0200 Subject: [PATCH 18/85] docs: updated prometheus docs --- docs/sources/features/datasources/graphite.md | 10 +-- .../features/datasources/prometheus.md | 82 +++++++++---------- docs/sources/reference/templating.md | 7 ++ .../prometheus/partials/query.editor.html | 20 +++-- 4 files changed, 66 insertions(+), 53 deletions(-) diff --git a/docs/sources/features/datasources/graphite.md b/docs/sources/features/datasources/graphite.md index 34fb48618f7..3e4276ba486 100644 --- a/docs/sources/features/datasources/graphite.md +++ b/docs/sources/features/datasources/graphite.md @@ -18,15 +18,13 @@ change function parameters and much more. The editor can handle all types of gra queries through the use of query references. ## Adding the data source -![](/img/docs/v2/add_Graphite.jpg) 1. Open the side menu by clicking the the Grafana icon in the top header. 2. In the side menu under the `Dashboards` link you should find a link named `Data Sources`. +3. Click the `+ Add data source` button in the top header. +4. Select `Graphite` from the *Type* dropdown. - > NOTE: If this link is missing in the side menu it means that your current user does not have the `Admin` role for the current organization. - -3. Click the `Add new` link in the top header. -4. Select `Graphite` from the dropdown. +> NOTE: If your not seeing the `Data Sources` link in your side menu it means that your current user does not have the `Admin` role for the current organization. Name | Description ------------ | ------------- @@ -84,7 +82,7 @@ being displayed in your dashboard. Checkout the [Templating]({{< relref "reference/templating.md" >}}) documentation for an introduction to the templating feature and the different types of template variables. -### Query variables +### Query variable The query you specify in the query field should be a metric find type of query. For example a query like `prod.servers.*` will fill the variable with all possible values that exists in the wildcard position. diff --git a/docs/sources/features/datasources/prometheus.md b/docs/sources/features/datasources/prometheus.md index e6f2749bb4b..b302fb3b5f1 100644 --- a/docs/sources/features/datasources/prometheus.md +++ b/docs/sources/features/datasources/prometheus.md @@ -17,70 +17,70 @@ Grafana includes support for Prometheus Datasources. While the process of adding ## Adding the data source to Grafana -![](/img/docs/v2/add_Prometheus.png) - 1. Open the side menu by clicking the the Grafana icon in the top header. 2. In the side menu under the `Dashboards` link you should find a link named `Data Sources`. +3. Click the `+ Add data source` button in the top header. +4. Select `Prometheus` from the *Type* dropdown. - > NOTE: If this link is missing in the side menu it means that your current user does not have the `Admin` role for the current organization. +> NOTE: If your not seeing the `Data Sources` link in your side menu it means that your current user does not have the `Admin` role for the current organization. -3. Click the `Add new` link in the top header. -4. Select `Prometheus` from the dropdown. +## Data source options Name | Description ------------ | ------------- -Name | The data source name, important that this is the same as in Grafana v1.x if you plan to import old dashboards. -Default | Default data source means that it will be pre-selected for new panels. -Url | The http protocol, ip and port of you Prometheus server (default port is usually 9090) -Access | Proxy = access via Grafana backend, Direct = access directly from browser. -Basic Auth | Enable basic authentication to the Prometheus datasource. -User | Name of your Prometheus user -Password | Database user's password - - > Proxy access means that the Grafana backend will proxy all requests from the browser, and send them on to the Data Source. This is useful because it can eliminate CORS (Cross Origin Site Resource) issues, as well as eliminate the need to disseminate authentication details to the Data Source to the browser. - - > Direct access is still supported because in some cases it may be useful to access a Data Source directly depending on the use case and topology of Grafana, the user, and the Data Source. +*Name* | The data source name, important that this is the same as in Grafana v1.x if you plan to import old dashboards. +*Default* | Default data source means that it will be pre-selected for new panels. +*Url* | The http protocol, ip and port of you Prometheus server (default port is usually 9090) +*Access* | Proxy = access via Grafana backend, Direct = access directly from browser. +*Basic Auth* | Enable basic authentication to the Prometheus datasource. +*User* | Name of your Prometheus user +*Password* | Database user's password ## Query editor -Open a graph in edit mode by click the title. -![](/img/v2/prometheus_editor.png) +Open a graph in edit mode by click the title > Edit (or by pressing `e` key while hovering over panel). -For details on Prometheus metric queries check out the Prometheus documentation -- [Query Metrics - Prometheus documentation](http://prometheus.io/docs/querying/basics/). - -## Templated queries - -Prometheus Datasource Plugin provides the following functions in `Variables values query` field in Templating Editor to query `metric names` and `labels names` on the Prometheus server. +![](/img/docs/v43/prometheus_query_editor.png) Name | Description ------- | -------- -`label_values(label)` | Returns a list of label values for the `label` in every metric. -`label_values(metric, label)` | Returns a list of label values for the `label` in the specified metric. -`metrics(metric)` | Returns a list of metrics matching the specified `metric` regex. -`query_result(query)` | Returns a list of Prometheus query result for the `query`. +*Query expression* | Prometheus query expression, check out the [Prometheus documentation](http://prometheus.io/docs/querying/basics/). +*Legend format* | Controls the name of the time series, using name or pattern. For example `{{hostname}}` will be replaced with label value for the label `hostname`. +*Min step* | Set a lower limit for the prometheus step option. Step controls how big the jumps are when the Prometheus query engine performs range queries. Sadly there is no official prometheus documentation to link to for this very important option. +*Resolution* | Controls the step option. Small steps create high resolution graphs but can be slow over larger time ranges, lowering the resolution can speed things up. `1/2` will try to set step option to generate 1 data point over other pixel. A value of `1/10` will try to set step option so there is a data point every 10 pixels. -For details of `metric names` & `label names`, and `label values`, please refer to the [Prometheus documentation](http://prometheus.io/docs/concepts/data_model/#metric-names-and-labels). +## Templating -> Note: The part of queries is incompatible with the version before 2.6, if you specify like `foo.*`, please change like `metrics(foo.*)`. +Instead of hard-coding things like server, application and sensor name in you metric queries you can use variables in their place. +Variables are shown as dropdown select boxes at the top of the dashboard. These dropdowns makes it easy to change the data +being displayed in your dashboard. -You can create a template variable in Grafana and have that variable filled with values from any Prometheus metric exploration query. -You can then use this variable in your Prometheus metric queries. +Checkout the [Templating]({{< relref "reference/templating.md" >}}) documentation for an introduction to the templating feature and the different +types of template variables. -For example you can have a variable that contains all values for label `hostname` if you specify a query like this in the templating edit view. +### Query variable -```sql -label_values(hostname) -``` +Variable of the type *Query* allows you to query Prometheus for a list of metrics, labels or label values. The Prometheus data source plugin +provides the following functions you can use in the `Query` input field. -You can also use raw queries & regular expressions to extract anything you might need. +Name | Description +---- | -------- +*label_values(label)* | Returns a list of label values for the `label` in every metric. +*label_values(metric, label)* | Returns a list of label values for the `label` in the specified metric. +*metrics(metric)* | Returns a list of metrics matching the specified `metric` regex. +*query_result(query)* | Returns a list of Prometheus query result for the `query`. -### Using templated variables in queries +For details of *metric names*, *label names* and *label values* are please refer to the [Prometheus documentation](http://prometheus.io/docs/concepts/data_model/#metric-names-and-labels). -When the `Include All` option or `Multi-Value` option is enabled, Grafana converts the labels from plain text to a regex compatible string. -Which means you have to use `=~` instead of `=` in your Prometheus queries. For example `ALERTS{instance=~$instance}` instead of `ALERTS{instance=$instance}`. +### Using variables in queries -![](/img/docs/v2/prometheus_templating.png) +There are two syntaxes: + +- `$` Example: rate(http_requests_total{job=~"$job"}[5m]) +- `[[varname]]` Example: rate(http_requests_total{job="my[[job]]"}[5m]) + +Why two ways? The first syntax is easier to read and write but does not allow you to use a variable in the middle of word. When the *Multi-value* or *Include all value* +option is enabled, Grafana converts the labels from plain text to a regex compatible string. Which means you have to use `=~` instead of `=`. ## Annotations diff --git a/docs/sources/reference/templating.md b/docs/sources/reference/templating.md index f16e03c898b..51aff771115 100644 --- a/docs/sources/reference/templating.md +++ b/docs/sources/reference/templating.md @@ -10,6 +10,13 @@ weight = 1 # Templating +Instead of hard-coding things like server, application and sensor name in you metric queries you can use variables in their place. +Variables are shown as dropdown select boxes at the top of the dashboard. These dropdowns makes it easy to change the data +being displayed in your dashboard. + +Checkout the [Templating]({{< relref "reference/templating.md" >}}) documentation for an introduction to the templating feature and the different +types of template variables. + Templating allows for more interactive and dynamic dashboards. Instead of hard-coding things like server, application diff --git a/public/app/plugins/datasource/prometheus/partials/query.editor.html b/public/app/plugins/datasource/prometheus/partials/query.editor.html index f919800b813..bd3d06773a6 100644 --- a/public/app/plugins/datasource/prometheus/partials/query.editor.html +++ b/public/app/plugins/datasource/prometheus/partials/query.editor.html @@ -13,9 +13,10 @@ ng-model-onblur ng-change="ctrl.refreshMetricData()">
+
- - Min step +
+
@@ -36,14 +38,20 @@
-
- +
+
+
+
+ +
+
+
- -
+ +
- + Reset
- + Reset From dbcd19bbce42ee2fd71d4198a62950e294ccd14e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Thu, 4 May 2017 12:12:55 +0200 Subject: [PATCH 26/85] docs: minor update to OpenTSDB docs --- docs/sources/features/datasources/opentsdb.md | 82 ++++++++++++------- 1 file changed, 51 insertions(+), 31 deletions(-) diff --git a/docs/sources/features/datasources/opentsdb.md b/docs/sources/features/datasources/opentsdb.md index 2b1c7e5c3f5..03795473ff7 100644 --- a/docs/sources/features/datasources/opentsdb.md +++ b/docs/sources/features/datasources/opentsdb.md @@ -12,59 +12,79 @@ weight = 5 # Using OpenTSDB in Grafana -{{< docs-imagebox img="/img/docs/v2/add_OpenTSDB.png" max-width="14rem" >}} +Grafana ships with advanced support for OpenTSDB. -The newest release of Grafana adds additional functionality when using an OpenTSDB Data source. +## Adding the data source -1. Open the side menu by clicking the the Grafana icon in the top header. +1. Open the side menu by clicking the Grafana icon in the top header. 2. In the side menu under the `Dashboards` link you should find a link named `Data Sources`. +3. Click the `+ Add data source` button in the top header. +4. Select *OpenTSDB* from the *Type* dropdown. - > NOTE: If this link is missing in the side menu it means that your current user does not have the `Admin` role for the current organization. - -3. Click the `Add new` link in the top header. -4. Select `OpenTSDB` from the dropdown. +> NOTE: If you're not seeing the `Data Sources` link in your side menu it means that your current user does not have the `Admin` role for the current organization. Name | Description ------------ | ------------- -Name | The data source name, important that this is the same as in Grafana v1.x if you plan to import old dashboards. -Default | Default data source means that it will be pre-selected for new panels. -Url | The http protocol, ip and port of you opentsdb server (default port is usually 4242) -Access | Proxy = access via Grafana backend, Direct = access directly from browser. -Version | Version = opentsdb version, either <=2.1 or 2.2 -Resolution | Metrics from opentsdb may have datapoints with either second or millisecond resolution. +*Name* | The data source name. This is how you refer to the data source in panels & queries. +*Default* | Default data source means that it will be pre-selected for new panels. +*Url* | The http protocol, ip and port of you opentsdb server (default port is usually 4242) +*Access* | Proxy = access via Grafana backend, Direct = access directly from browser. +*Version* | Version = opentsdb version, either <=2.1 or 2.2 +*Resolution* | Metrics from opentsdb may have datapoints with either second or millisecond resolution. + ## Query editor -Open a graph in edit mode by click the title. Query editor will differ if the datasource has version <=2.1 or = 2.2. In the former version, only tags can be used to query opentsdb. But in the latter version, filters as well as tags can be used to query opentsdb. Fill Policy is also introduced in opentsdb 2.2. - > Note: While using Opentsdb 2.2 datasource, make sure you use either Filters or Tags as they are mutually exclusive. If used together, might give you weird results. +Open a graph in edit mode by click the title. Query editor will differ if the datasource has version <=2.1 or = 2.2. +In the former version, only tags can be used to query OpenTSDB. But in the latter version, filters as well as tags +can be used to query opentsdb. Fill Policy is also introduced in OpenTSDB 2.2. -![](/img/docs/v2/opentsdb_query_editor.png) +![](/img/docs/v43/opentsdb_query_editor.png) + +> Note: While using OpenTSDB 2.2 datasource, make sure you use either Filters or Tags as they are mutually exclusive. If used together, might give you weird results. ### Auto complete suggestions -As soon as you start typing metric names, tag names and tag values , you should see highlighted auto complete suggestions for them. - > Note: This is required for the OpenTSDB `suggest` api to work. +As soon as you start typing metric names, tag names and tag values , you should see highlighted auto complete suggestions for them. +The autocomplete only works if the OpenTSDB suggest api is enabled. ## Templating queries -Grafana's OpenTSDB data source now supports template variable values queries. This means you can create template variables that fetch the values from OpenTSDB (for example metric names, tag names, or tag values). The query editor is also enhanced to limiting tags by metric. + +Instead of hard-coding things like server, application and sensor name in you metric queries you can use variables in their place. +Variables are shown as dropdown select boxes at the top of the dashboard. These dropdowns makes it easy to change the data +being displayed in your dashboard. + +Checkout the [Templating]({{< relref "reference/templating.md" >}}) documentation for an introduction to the templating feature and the different +types of template variables. + +### Query variable + +Grafana's OpenTSDB data source supports template variable queries. This means you can create template variables +that fetch the values from OpenTSDB. For example, metric names, tag names, or tag values. When using OpenTSDB with a template variable of `query` type you can use following syntax for lookup. - metrics(prefix) // returns metric names with specific prefix (can be empty) - tag_names(cpu) // return tag names (i.e. keys) for a specific cpu metric - tag_values(cpu, hostname) // return tag values for metric cpu and tag key hostname - suggest_tagk(prefix) // return tag names (i.e. keys) for all metrics with specific prefix (can be empty) - suggest_tagv(prefix) // return tag values for all metrics with specific prefix (can be empty) +Query | Description +------------ | ------------- +*metrics(prefix)* | Returns metric names with specific prefix (can be empty) +*tag_names(cpu)* | Return tag names (i.e. keys) for a specific cpu metric +*tag_values(cpu, hostname)* | Return tag values for metric cpu and tag key hostname +*suggest_tagk(prefix)* | Return tag names (i.e. keys) for all metrics with specific prefix (can be empty) +*suggest_tagv(prefix)* | Return tag values for all metrics with specific prefix (can be empty) -If you do not see template variables being populated in `Preview of values` section, you need to enable `tsd.core.meta.enable_realtime_ts` in the OpenTSDB server settings. Also, to populate metadata of the existing time series data in OpenTSDB, you need to run `tsdb uid metasync` on the OpenTSDB server. +If you do not see template variables being populated in `Preview of values` section, you need to enable +`tsd.core.meta.enable_realtime_ts` in the OpenTSDB server settings. Also, to populate metadata of +the existing time series data in OpenTSDB, you need to run `tsdb uid metasync` on the OpenTSDB server. ### Nested Templating -One template variable can be used to filter tag values for another template varible. Very importantly, the order of the parameters matter in tag_values function. First parameter is the metric name, second parameter is the tag key for which you need to find tag values, and after that all other dependent template variables. Some examples are mentioned below to make nested template queries work successfully. +One template variable can be used to filter tag values for another template varible. First parameter is the metric name, +second parameter is the tag key for which you need to find tag values, and after that all other dependent template variables. +Some examples are mentioned below to make nested template queries work successfully. - tag_values(cpu, hostname, env=$env) // return tag values for cpu metric, selected env tag value and tag key hostname - tag_values(cpu, hostanme, env=$env, region=$region) // return tag values for cpu metric, selected env tag value, selected region tag value and tag key hostname +Query | Description +------------ | ------------- +*tag_values(cpu, hostname, env=$env)* | Return tag values for cpu metric, selected env tag value and tag key hostname +*tag_values(cpu, hostanme, env=$env, region=$region)* | Return tag values for cpu metric, selected env tag value, selected region tag value and tag key hostname -> Note: This is required for the OpenTSDB `lookup` api to work. - -For details on opentsdb metric queries checkout the official [OpenTSDB documentation](http://opentsdb.net/docs/build/html/index.html) +For details on OpenTSDB metric queries checkout the official [OpenTSDB documentation](http://opentsdb.net/docs/build/html/index.html) From 0390a7c17bbfb28dc61dd2484ca7dc8ea4bff833 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Thu, 4 May 2017 12:36:34 +0200 Subject: [PATCH 27/85] docs: fixed docs intro --- docs/sources/reference/templating.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/docs/sources/reference/templating.md b/docs/sources/reference/templating.md index 128af6591d2..79d9e6656e0 100644 --- a/docs/sources/reference/templating.md +++ b/docs/sources/reference/templating.md @@ -14,9 +14,6 @@ Instead of hard-coding things like server, application and sensor name in you me Variables are shown as dropdown select boxes at the top of the dashboard. These dropdowns makes it easy to change the data being displayed in your dashboard. -Checkout the [Templating]({{< relref "reference/templating.md" >}}) documentation for an introduction to the templating feature and the different -types of template variables. - Templating allows for more interactive and dynamic dashboards. Instead of hard-coding things like server, application From 61b51c0cbf09834a22ca0f228ecb0443b1e3d513 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Thu, 4 May 2017 16:03:47 +0200 Subject: [PATCH 28/85] heatmap: refactoring --- .../app/plugins/panel/heatmap/axes_editor.ts | 24 +++---------- .../panel/heatmap/partials/axes_editor.html | 36 +++++++++++-------- 2 files changed, 25 insertions(+), 35 deletions(-) diff --git a/public/app/plugins/panel/heatmap/axes_editor.ts b/public/app/plugins/panel/heatmap/axes_editor.ts index 2bf4ac05622..32ea66cc4d9 100644 --- a/public/app/plugins/panel/heatmap/axes_editor.ts +++ b/public/app/plugins/panel/heatmap/axes_editor.ts @@ -8,8 +8,7 @@ export class AxesEditorCtrl { unitFormats: any; logScales: any; dataFormats: any; - yBucketOptions: any[]; - xBucketOptions: any[]; + yBucketModes: any[]; /** @ngInject */ constructor($scope, uiSegmentSrv) { @@ -31,24 +30,9 @@ export class AxesEditorCtrl { 'Time series Pre-bucketed': 'tsbuckets' }; - this.yBucketOptions = [ - {text: '5', value: '5'}, - {text: '10', value: '10'}, - {text: '20', value: '20'}, - {text: '30', value: '30'}, - {text: '50', value: '50'}, - ]; - - this.xBucketOptions = [ - {text: '15', value: '15'}, - {text: '20', value: '20'}, - {text: '30', value: '30'}, - {text: '50', value: '50'}, - {text: '1m', value: '1m'}, - {text: '5m', value: '5m'}, - {text: '10m', value: '10m'}, - {text: '20m', value: '20m'}, - {text: '1h', value: '1h'}, + this.yBucketModes = [ + {text: 'Count', value: 'count'}, + {text: 'Interval', value: 'interval'}, ]; } diff --git a/public/app/plugins/panel/heatmap/partials/axes_editor.html b/public/app/plugins/panel/heatmap/partials/axes_editor.html index a8dd1a05473..933f3f08df1 100644 --- a/public/app/plugins/panel/heatmap/partials/axes_editor.html +++ b/public/app/plugins/panel/heatmap/partials/axes_editor.html @@ -3,7 +3,7 @@
Y Axis
-
@@ -11,19 +11,17 @@
-
+
-
-
- - -
-
- - -
+
+ + +
+
+ +
@@ -34,10 +32,18 @@
- - + +
+ +
+ + + Number of buckets for Y axis
From b4a8678cae1e3e3170604d915d17bb2dde10eea3 Mon Sep 17 00:00:00 2001 From: Trent White Date: Thu, 4 May 2017 10:34:19 -0400 Subject: [PATCH 29/85] remove duplicate paragraph, couple word tweaks --- docs/sources/reference/templating.md | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/docs/sources/reference/templating.md b/docs/sources/reference/templating.md index 79d9e6656e0..7fa71989570 100644 --- a/docs/sources/reference/templating.md +++ b/docs/sources/reference/templating.md @@ -10,16 +10,12 @@ weight = 1 # Templating -Instead of hard-coding things like server, application and sensor name in you metric queries you can use variables in their place. -Variables are shown as dropdown select boxes at the top of the dashboard. These dropdowns makes it easy to change the data -being displayed in your dashboard. - - - Templating allows for more interactive and dynamic dashboards. Instead of hard-coding things like server, application and sensor name in you metric queries you can use variables in their place. Variables are shown as dropdown select boxes at the top of the dashboard. These dropdowns make it easy to change the data being displayed in your dashboard. + + ## What is a variable? A variable is a placeholder for a value. You can use variables in metric queries and in panel titles. So when you change @@ -42,7 +38,7 @@ documentation article for details on value escaping during interpolation. ### Variable options -A variable is presented as a dropdown select box at the top of the dashboards. It has a current value and a set of **options**. The **options** +A variable is presented as a dropdown select box at the top of the dashboard. It has a current value and a set of **options**. The **options** is the set of values you can choose from. ## Adding a variable @@ -151,7 +147,7 @@ Template variables can be very useful to dynamically change your queries across Grafana to dynamically create new panels or rows based on what values you have selected you can use the *Repeat* feature. If you have a variable with `Multi-value` or `Include all value` options enabled you can choose one panel or one row and have Grafana repeat that row -for every selected value. You find this option under the General tab in panel edit mode. You select the variable to repeat by, and a `min span`. +for every selected value. You find this option under the General tab in panel edit mode. Select the variable to repeat by, and a `min span`. The `min span` controls how small Grafana will make the panels (if you have many values selected). Grafana will automatically adjust the width of each repeated panel so that the whole row is filled. Currently, you cannot mix other panels on a row with a repeated panel. From 5acabc6ccbfab78b7441e2894e0ba7513025ac29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Thu, 4 May 2017 19:56:20 +0200 Subject: [PATCH 30/85] heatmap: refactoring --- .../app/plugins/panel/heatmap/axes_editor.ts | 6 -- .../app/plugins/panel/heatmap/heatmap_ctrl.ts | 12 ++- .../panel/heatmap/partials/axes_editor.html | 84 +++++++++++-------- 3 files changed, 58 insertions(+), 44 deletions(-) diff --git a/public/app/plugins/panel/heatmap/axes_editor.ts b/public/app/plugins/panel/heatmap/axes_editor.ts index 32ea66cc4d9..46926697522 100644 --- a/public/app/plugins/panel/heatmap/axes_editor.ts +++ b/public/app/plugins/panel/heatmap/axes_editor.ts @@ -8,7 +8,6 @@ export class AxesEditorCtrl { unitFormats: any; logScales: any; dataFormats: any; - yBucketModes: any[]; /** @ngInject */ constructor($scope, uiSegmentSrv) { @@ -29,11 +28,6 @@ export class AxesEditorCtrl { 'Time series': 'timeseries', 'Time series Pre-bucketed': 'tsbuckets' }; - - this.yBucketModes = [ - {text: 'Count', value: 'count'}, - {text: 'Interval', value: 'interval'}, - ]; } setUnitFormat(subItem) { diff --git a/public/app/plugins/panel/heatmap/heatmap_ctrl.ts b/public/app/plugins/panel/heatmap/heatmap_ctrl.ts index 8a7672f7a23..5c5f8b33efb 100644 --- a/public/app/plugins/panel/heatmap/heatmap_ctrl.ts +++ b/public/app/plugins/panel/heatmap/heatmap_ctrl.ts @@ -30,7 +30,11 @@ let panelDefaults = { dataFormat: 'timeseries', xAxis: { show: true, - buckets: 'auto', + buckets: { + mode: 'count', + count: null, + size: null, + }, }, yAxis: { show: true, @@ -40,7 +44,11 @@ let panelDefaults = { splitFactor: null, min: null, max: null, - buckets: 'auto', + buckets: { + mode: 'count', + count: null, + size: null, + }, removeZeroValues: false }, tooltip: { diff --git a/public/app/plugins/panel/heatmap/partials/axes_editor.html b/public/app/plugins/panel/heatmap/partials/axes_editor.html index 933f3f08df1..5841470f69c 100644 --- a/public/app/plugins/panel/heatmap/partials/axes_editor.html +++ b/public/app/plugins/panel/heatmap/partials/axes_editor.html @@ -4,9 +4,9 @@
+ ng-model="ctrl.panel.yAxis.format" + dropdown-typeahead2="editor.unitFormats" + dropdown-typeahead-on-select="editor.setUnitFormat($subItem)">
@@ -26,54 +26,66 @@
+ bs-tooltip="'Override automatic decimal precision for axis.'" + ng-model="ctrl.panel.yAxis.decimals" ng-change="ctrl.render()" ng-model-onblur>
+
+ +
+
Buckets
- -
- -
- - - Number of buckets for Y axis + + + +
+
+ + +
+
+
+
+ + + +
+
+ +
- - + + +
-
X Axis
+
Data format
- - -
-
- -
-
Data format
-
- -
- + +
+ +
-
From 6ee11f1172fcc274de93f9126d37c8cd7b8cc345 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Fri, 5 May 2017 07:05:11 +0200 Subject: [PATCH 31/85] heatmap: minor update --- public/app/plugins/panel/heatmap/axes_editor.ts | 2 +- public/app/plugins/panel/heatmap/heatmap_ctrl.ts | 14 ++++---------- 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/public/app/plugins/panel/heatmap/axes_editor.ts b/public/app/plugins/panel/heatmap/axes_editor.ts index 46926697522..3c905d967f8 100644 --- a/public/app/plugins/panel/heatmap/axes_editor.ts +++ b/public/app/plugins/panel/heatmap/axes_editor.ts @@ -26,7 +26,7 @@ export class AxesEditorCtrl { this.dataFormats = { 'Time series': 'timeseries', - 'Time series Pre-bucketed': 'tsbuckets' + 'Time series buckets': 'tsbuckets' }; } diff --git a/public/app/plugins/panel/heatmap/heatmap_ctrl.ts b/public/app/plugins/panel/heatmap/heatmap_ctrl.ts index 5c5f8b33efb..8e4b5bc1e54 100644 --- a/public/app/plugins/panel/heatmap/heatmap_ctrl.ts +++ b/public/app/plugins/panel/heatmap/heatmap_ctrl.ts @@ -30,11 +30,6 @@ let panelDefaults = { dataFormat: 'timeseries', xAxis: { show: true, - buckets: { - mode: 'count', - count: null, - size: null, - }, }, yAxis: { show: true, @@ -44,13 +39,12 @@ let panelDefaults = { splitFactor: null, min: null, max: null, - buckets: { - mode: 'count', - count: null, - size: null, - }, removeZeroValues: false }, + xBucketSize: null, + xBucketNumber: null, + yBucketSize: null, + yBucketNumber: null, tooltip: { show: true, seriesStat: false, From e75bc5d39a35c389ab9d196d67848629a3200ce6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Fri, 5 May 2017 08:56:19 +0200 Subject: [PATCH 32/85] docs: added templating docs link to app --- docs/sources/reference/templating.md | 4 ++-- .../features/templating/partials/editor.html | 22 ++++++++++++++++++- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/docs/sources/reference/templating.md b/docs/sources/reference/templating.md index 7fa71989570..0405251f44d 100644 --- a/docs/sources/reference/templating.md +++ b/docs/sources/reference/templating.md @@ -90,8 +90,8 @@ The query expressions are different for each data source. - [Prometheus templating queries]({{< relref "features/datasources/prometheus.md#templating" >}}) - [OpenTSDB templating queries]({{< relref "features/datasources/prometheus.md#templating" >}}) -One thing to note is that query expressions can contain references to other variables and in effect create depend & nested -variables. Grafana will detect this and automatically refresh a variable when one of it's containing variables change. +One thing to note is that query expressions can contain references to other variables and in effect create linked variables. +Grafana will detect this and automatically refresh a variable when one of it's containing variables change. ## Selection Options diff --git a/public/app/features/templating/partials/editor.html b/public/app/features/templating/partials/editor.html index a0683debc5c..59e304278f7 100644 --- a/public/app/features/templating/partials/editor.html +++ b/public/app/features/templating/partials/editor.html @@ -17,6 +17,11 @@
  • New +
  • +
  • + + Help +
  • @@ -30,7 +35,8 @@
    No template variables defined -
    +

    +
    @@ -64,6 +70,20 @@
    +
    +
    +

    What does templating do?

    +

    Templating allows for more interactive and dynamic dashboards. Instead of hard-coding things like server, application + and sensor name in you metric queries you can use variables in their place. Variables are shown as dropdown select boxes at the top of + the dashboard. These dropdowns make it easy to change the data being displayed in your dashboard. +
    +
    + + Checkout the Templating documentation for more information. +

    +
    +
    +
      New From 2479ad262ea47643fec6c904b9486c9528d5efc1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Fri, 5 May 2017 09:16:28 +0200 Subject: [PATCH 33/85] ux: updated look of info box --- public/app/features/templating/partials/editor.html | 2 +- public/app/partials/metrics.html | 1 + public/sass/components/_infobox.scss | 8 +++++--- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/public/app/features/templating/partials/editor.html b/public/app/features/templating/partials/editor.html index 59e304278f7..d974b7d4e3d 100644 --- a/public/app/features/templating/partials/editor.html +++ b/public/app/features/templating/partials/editor.html @@ -72,7 +72,7 @@
    -

    What does templating do?

    +
    What does templating do?

    Templating allows for more interactive and dynamic dashboards. Instead of hard-coding things like server, application and sensor name in you metric queries you can use variables in their place. Variables are shown as dropdown select boxes at the top of the dashboard. These dropdowns make it easy to change the data being displayed in your dashboard. diff --git a/public/app/partials/metrics.html b/public/app/partials/metrics.html index 1cd6718e27b..39c471eabd1 100644 --- a/public/app/partials/metrics.html +++ b/public/app/partials/metrics.html @@ -17,3 +17,4 @@

    +
    diff --git a/public/sass/components/_infobox.scss b/public/sass/components/_infobox.scss index e6d42597c75..a6951ee9c51 100644 --- a/public/sass/components/_infobox.scss +++ b/public/sass/components/_infobox.scss @@ -10,9 +10,11 @@ .grafana-info-box { position: relative; - padding: 5px 20px; - background-color: $tight-form-bg; - border: 1px solid $tight-form-border; + background: $card-background; + box-shadow: $card-shadow; + padding: 1rem; + border-radius: 4px; + h5 { margin-top: 5px; } From 5c9810fba46ffb0eb95a41b1af45a1e78f6e2aaa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Fri, 5 May 2017 11:30:44 +0200 Subject: [PATCH 34/85] docs: added in app docs & links to annotation docs --- .../features/annotations/partials/editor.html | 21 +++++++++++++++++++ public/sass/components/_infobox.scss | 2 +- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/public/app/features/annotations/partials/editor.html b/public/app/features/annotations/partials/editor.html index b9032cdcd87..1506e1a0dc5 100644 --- a/public/app/features/annotations/partials/editor.html +++ b/public/app/features/annotations/partials/editor.html @@ -18,6 +18,12 @@
  • New Query
  • + +
  • + + Help + +
  • + +
    +
    +
    What are Annotations?
    +

    + Annotations provide a way to integrate event data into your graphs. They are visualized as vertical lines and icons + on all graph panels. When you hover over an annotation icon you can get title, tags, and text information for the event. + In the Queries tab you can add queries that return annotation events. +
    +
    + Checkout the Annotations documentation for more information. +

    +
    +
    +
    No annotation queries defined diff --git a/public/sass/components/_infobox.scss b/public/sass/components/_infobox.scss index a6951ee9c51..63b334a273e 100644 --- a/public/sass/components/_infobox.scss +++ b/public/sass/components/_infobox.scss @@ -16,7 +16,7 @@ border-radius: 4px; h5 { - margin-top: 5px; + margin-bottom: $spacer; } ul { padding-left: $spacer; From 4412e41738beaddc8cf2078f7653329f24e9e012 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Fri, 5 May 2017 11:43:42 +0200 Subject: [PATCH 35/85] ux: updated heatmap and alertlist icons --- .../alertlist/img/icn-singlestat-panel.svg | 108 ++++++--- .../panel/heatmap/img/icn-heatmap-panel.svg | 214 +++++++----------- 2 files changed, 156 insertions(+), 166 deletions(-) diff --git a/public/app/plugins/panel/alertlist/img/icn-singlestat-panel.svg b/public/app/plugins/panel/alertlist/img/icn-singlestat-panel.svg index a1e15d4d58d..d6ba04bbf01 100644 --- a/public/app/plugins/panel/alertlist/img/icn-singlestat-panel.svg +++ b/public/app/plugins/panel/alertlist/img/icn-singlestat-panel.svg @@ -1,33 +1,75 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + diff --git a/public/app/plugins/panel/heatmap/img/icn-heatmap-panel.svg b/public/app/plugins/panel/heatmap/img/icn-heatmap-panel.svg index 932d226b99a..54e5f5d44f2 100644 --- a/public/app/plugins/panel/heatmap/img/icn-heatmap-panel.svg +++ b/public/app/plugins/panel/heatmap/img/icn-heatmap-panel.svg @@ -3,138 +3,86 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 43c6f749047275c9c1db192e88e08701930366f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Fri, 5 May 2017 12:33:04 +0200 Subject: [PATCH 36/85] heatmap: removed fill background, and highlight card options --- .../app/plugins/panel/heatmap/heatmap_ctrl.ts | 1 - .../plugins/panel/heatmap/heatmap_tooltip.ts | 2 +- .../heatmap/partials/display_editor.html | 26 ++- public/app/plugins/panel/heatmap/rendering.ts | 169 ++++++++---------- 4 files changed, 86 insertions(+), 112 deletions(-) diff --git a/public/app/plugins/panel/heatmap/heatmap_ctrl.ts b/public/app/plugins/panel/heatmap/heatmap_ctrl.ts index 8e4b5bc1e54..5600a5ea2e4 100644 --- a/public/app/plugins/panel/heatmap/heatmap_ctrl.ts +++ b/public/app/plugins/panel/heatmap/heatmap_ctrl.ts @@ -25,7 +25,6 @@ let panelDefaults = { colorScale: 'sqrt', exponent: 0.5, colorScheme: 'interpolateOranges', - fillBackground: false }, dataFormat: 'timeseries', xAxis: { diff --git a/public/app/plugins/panel/heatmap/heatmap_tooltip.ts b/public/app/plugins/panel/heatmap/heatmap_tooltip.ts index c78fb35cfba..fe36112b859 100644 --- a/public/app/plugins/panel/heatmap/heatmap_tooltip.ts +++ b/public/app/plugins/panel/heatmap/heatmap_tooltip.ts @@ -87,7 +87,7 @@ export class HeatmapTooltip { let tooltipHtml = `
    ${time}
    `; - if (yData) { + if (yData && yData.bounds) { boundBottom = valueFormatter(yData.bounds.bottom); boundTop = valueFormatter(yData.bounds.top); valuesNumber = yData.values.length; diff --git a/public/app/plugins/panel/heatmap/partials/display_editor.html b/public/app/plugins/panel/heatmap/partials/display_editor.html index 92409b3ad5e..a6d1de1981c 100644 --- a/public/app/plugins/panel/heatmap/partials/display_editor.html +++ b/public/app/plugins/panel/heatmap/partials/display_editor.html @@ -2,51 +2,49 @@
    Colors
    - -
    + +
    - +
    - +
    - +
    - +
    - -
    + +
    - +
    - -
    -
    Cards
    +
    Buckets
    @@ -64,10 +62,6 @@ checked="ctrl.panel.tooltip.show" on-change="ctrl.render()">
    - - diff --git a/public/app/plugins/panel/heatmap/rendering.ts b/public/app/plugins/panel/heatmap/rendering.ts index a0f6786baf1..b822de2b068 100644 --- a/public/app/plugins/panel/heatmap/rendering.ts +++ b/public/app/plugins/panel/heatmap/rendering.ts @@ -376,10 +376,6 @@ export default function link(scope, elem, attrs, ctrl) { setOpacityScale(max_value); setCardSize(); - if (panel.color.fillBackground && panel.color.mode === 'spectrum') { - fillBackground(heatmap, colorScale(0)); - } - let cards = heatmap.selectAll(".heatmap-card").data(cardsData); cards.append("title"); cards = cards.enter().append("rect") @@ -407,24 +403,20 @@ export default function link(scope, elem, attrs, ctrl) { } function highlightCard(event) { - if (panel.highlightCards) { - let color = d3.select(event.target).style("fill"); - let highlightColor = d3.color(color).darker(2); - let strokeColor = d3.color(color).brighter(4); - let current_card = d3.select(event.target); - tooltip.originalFillColor = color; - current_card.style("fill", highlightColor) - .style("stroke", strokeColor) - .style("stroke-width", 1); - } + let color = d3.select(event.target).style("fill"); + let highlightColor = d3.color(color).darker(2); + let strokeColor = d3.color(color).brighter(4); + let current_card = d3.select(event.target); + tooltip.originalFillColor = color; + current_card.style("fill", highlightColor) + .style("stroke", strokeColor) + .style("stroke-width", 1); } function resetCardHighLight(event) { - if (panel.highlightCards) { - d3.select(event.target).style("fill", tooltip.originalFillColor) - .style("stroke", tooltip.originalFillColor) - .style("stroke-width", 0); - } + d3.select(event.target).style("fill", tooltip.originalFillColor) + .style("stroke", tooltip.originalFillColor) + .style("stroke-width", 0); } function getColorScale(maxValue) { @@ -442,12 +434,12 @@ export default function link(scope, elem, attrs, ctrl) { function setOpacityScale(max_value) { if (panel.color.colorScale === 'linear') { opacityScale = d3.scaleLinear() - .domain([0, max_value]) - .range([0, 1]); + .domain([0, max_value]) + .range([0, 1]); } else if (panel.color.colorScale === 'sqrt') { opacityScale = d3.scalePow().exponent(panel.color.exponent) - .domain([0, max_value]) - .range([0, 1]); + .domain([0, max_value]) + .range([0, 1]); } } @@ -549,15 +541,6 @@ export default function link(scope, elem, attrs, ctrl) { } } - function fillBackground(heatmap, color) { - heatmap.insert("rect", "g") - .attr("x", yAxisWidth) - .attr("y", margin.top) - .attr("width", chartWidth) - .attr("height", chartHeight) - .attr("fill", color); - } - ///////////////////////////// // Selection and crosshair // ///////////////////////////// @@ -570,12 +553,12 @@ export default function link(scope, elem, attrs, ctrl) { if (ctrl.dashboard.graphTooltip === 2) { tooltip.show(event.pos, data); } - }); + }, scope); appEvents.on('graph-hover-clear', () => { clearCrosshair(); tooltip.destroy(); - }); + }, scope); function onMouseDown(event) { selection.active = true; @@ -584,6 +567,7 @@ export default function link(scope, elem, attrs, ctrl) { mouseUpHandler = function() { onMouseUp(); }; + $(document).one("mouseup", mouseUpHandler); } @@ -660,11 +644,11 @@ export default function link(scope, elem, attrs, ctrl) { if (selectionWidth > MIN_SELECTION_WIDTH) { heatmap.append("rect") - .attr("class", "heatmap-selection") - .attr("x", selectionX) - .attr("width", selectionWidth) - .attr("y", chartTop) - .attr("height", chartHeight); + .attr("class", "heatmap-selection") + .attr("x", selectionX) + .attr("width", selectionWidth) + .attr("y", chartTop) + .attr("height", chartHeight); } } } @@ -687,14 +671,14 @@ export default function link(scope, elem, attrs, ctrl) { posX = Math.min(posX, chartWidth + yAxisWidth); heatmap.append("g") - .attr("class", "heatmap-crosshair") - .attr("transform", "translate(" + posX + ",0)") - .append("line") - .attr("x1", 1) - .attr("y1", chartTop) - .attr("x2", 1) - .attr("y2", chartBottom) - .attr("stroke-width", 1); + .attr("class", "heatmap-crosshair") + .attr("transform", "translate(" + posX + ",0)") + .append("line") + .attr("x1", 1) + .attr("y1", chartTop) + .attr("x2", 1) + .attr("y2", chartBottom) + .attr("stroke-width", 1); } } @@ -725,14 +709,14 @@ export default function link(scope, elem, attrs, ctrl) { var legendRects = legend.selectAll(".heatmap-color-legend-rect").data(valuesRange); legendRects.enter().append("rect") - .attr("x", d => d) - .attr("y", 0) - .attr("width", rangeStep + 1) // Overlap rectangles to prevent gaps - .attr("height", legendHeight) - .attr("stroke-width", 0) - .attr("fill", d => { - return legendColorScale(d); - }); + .attr("x", d => d) + .attr("y", 0) + .attr("width", rangeStep + 1) // Overlap rectangles to prevent gaps + .attr("height", legendHeight) + .attr("stroke-width", 0) + .attr("fill", d => { + return legendColorScale(d); + }); } function drawOpacityLegend() { @@ -745,12 +729,12 @@ export default function link(scope, elem, attrs, ctrl) { let legendOpacityScale; if (panel.color.colorScale === 'linear') { legendOpacityScale = d3.scaleLinear() - .domain([0, legendWidth]) - .range([0, 1]); + .domain([0, legendWidth]) + .range([0, 1]); } else if (panel.color.colorScale === 'sqrt') { legendOpacityScale = d3.scalePow().exponent(panel.color.exponent) - .domain([0, legendWidth]) - .range([0, 1]); + .domain([0, legendWidth]) + .range([0, 1]); } let rangeStep = 1; @@ -758,15 +742,15 @@ export default function link(scope, elem, attrs, ctrl) { var legendRects = legend.selectAll(".heatmap-opacity-legend-rect").data(valuesRange); legendRects.enter().append("rect") - .attr("x", d => d) - .attr("y", 0) - .attr("width", rangeStep) - .attr("height", legendHeight) - .attr("stroke-width", 0) - .attr("fill", panel.color.cardColor) - .style("opacity", d => { - return legendOpacityScale(d); - }); + .attr("x", d => d) + .attr("y", 0) + .attr("width", rangeStep) + .attr("height", legendHeight) + .attr("stroke-width", 0) + .attr("fill", panel.color.cardColor) + .style("opacity", d => { + return legendOpacityScale(d); + }); } function render() { @@ -774,34 +758,26 @@ export default function link(scope, elem, attrs, ctrl) { panel = ctrl.panel; timeRange = ctrl.range; - if (setElementHeight()) { - - if (data) { - // Draw default axes and return if no data - if (_.isEmpty(data.buckets)) { - addHeatmapCanvas(); - addAxes(); - return; - } - - addHeatmap(); - scope.yScale = yScale; - scope.xScale = xScale; - scope.yAxisWidth = yAxisWidth; - scope.xAxisHeight = xAxisHeight; - scope.chartHeight = chartHeight; - scope.chartWidth = chartWidth; - scope.chartTop = chartTop; - - // Register selection listeners - $heatmap.on("mousedown", onMouseDown); - $heatmap.on("mousemove", onMouseMove); - $heatmap.on("mouseleave", onMouseLeave); - } else { - return; - } + if (!setElementHeight() || !data) { + return; } + // Draw default axes and return if no data + if (_.isEmpty(data.buckets)) { + addHeatmapCanvas(); + addAxes(); + return; + } + + addHeatmap(); + scope.yScale = yScale; + scope.xScale = xScale; + scope.yAxisWidth = yAxisWidth; + scope.xAxisHeight = xAxisHeight; + scope.chartHeight = chartHeight; + scope.chartWidth = chartWidth; + scope.chartTop = chartTop; + // Draw only if color editor is opened if (!d3.select("#heatmap-color-legend").empty()) { drawColorLegend(); @@ -810,6 +786,11 @@ export default function link(scope, elem, attrs, ctrl) { drawOpacityLegend(); } } + + // Register selection listeners + $heatmap.on("mousedown", onMouseDown); + $heatmap.on("mousemove", onMouseMove); + $heatmap.on("mouseleave", onMouseLeave); } function grafanaTimeFormat(ticks, min, max) { From dd5a426911bae61b0a4dd4a00cc298411c17781e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Fri, 5 May 2017 16:15:08 +0200 Subject: [PATCH 37/85] heatmap: refactoring heatmap --- docs/sources/features/datasources/testdata.md | 11 +-- docs/sources/features/panels/heatmap.md | 16 ++++ .../panel/heatmap/heatmap_data_converter.ts | 80 +++++++------------ .../panel/heatmap/partials/axes_editor.html | 12 +-- public/app/plugins/panel/heatmap/rendering.ts | 25 +++--- .../specs/heatmap_data_converter_specs.ts | 14 ++-- 6 files changed, 71 insertions(+), 87 deletions(-) create mode 100644 docs/sources/features/panels/heatmap.md diff --git a/docs/sources/features/datasources/testdata.md b/docs/sources/features/datasources/testdata.md index 3e3f0909700..3475980d2b4 100644 --- a/docs/sources/features/datasources/testdata.md +++ b/docs/sources/features/datasources/testdata.md @@ -11,26 +11,20 @@ weight = 20 # Grafana TestData - > NOTE: This plugin is disable by default. - The purpose of this data sources is to make it easier to create fake data for any panel. Using `Grafana TestData` you can build your own time series and have any panel render it. This make is much easier to verify functionally since the data can be shared very ## Enable -`Grafana TestData` is not enabled by default. To enable it you have to go to `/plugins/testdata/edit` and click the enable button to enable it for each server. +`Grafana TestData` is not enabled by default. To enable it you have to go to `/plugins/testdata/edit` and click the enable button to enable. ## Create mock data. -Once `Grafana TestData` is enabled you use it as a datasource in the metric panel. +Once `Grafana TestData` is enabled you can use it as a data source in any metric panel. ![](/img/docs/v41/test_data_add.png) -## Scenarios - -You can now choose different scenario that you want rendered in the drop down menu. If you have scenarios that you think should be added, please add them to `` and submit a pull request. - ## CSV The comma separated values scenario is the most powerful one since it lets you create any kind of graph you like. @@ -38,7 +32,6 @@ Once you provided the numbers `Grafana TestData` will distribute them evenly bas ![](/img/docs/v41/test_data_csv_example.png) - ## Dashboards `Grafana TestData` also contains some dashboards with example. `/plugins/testdata/edit` diff --git a/docs/sources/features/panels/heatmap.md b/docs/sources/features/panels/heatmap.md new file mode 100644 index 00000000000..dd469394cd0 --- /dev/null +++ b/docs/sources/features/panels/heatmap.md @@ -0,0 +1,16 @@ ++++ +title = "Heatmap Panel" +description = "Heatmap panel documentation" +keywords = ["grafana", "heatmap", "panel", "documentation"] +type = "docs" +[menu.docs] +parent = "panels" +weight = 3 ++++ + +# Heatmap Panel + +> New panel only available in Grafana v4.3+ + +![](/img/docs/v43/heatmap_panel.png) + diff --git a/public/app/plugins/panel/heatmap/heatmap_data_converter.ts b/public/app/plugins/panel/heatmap/heatmap_data_converter.ts index ba1cd223584..28488c1faf8 100644 --- a/public/app/plugins/panel/heatmap/heatmap_data_converter.ts +++ b/public/app/plugins/panel/heatmap/heatmap_data_converter.ts @@ -16,56 +16,38 @@ interface YBucket { values: number[]; } -function elasticHistogramToHeatmap(series) { - let seriesBuckets = _.map(series, (s: TimeSeries) => { - return convertEsSeriesToHeatmap(s); - }); - let buckets = mergeBuckets(seriesBuckets); - return buckets; -} +function elasticHistogramToHeatmap(seriesList) { + let heatmap = {}; -function convertEsSeriesToHeatmap(series: TimeSeries, saveZeroCounts = false) { - let xBuckets: XBucket[] = []; - - _.forEach(series.datapoints, point => { - let bound = series.alias; - let count = point[VALUE_INDEX]; - - if (!count) { + for (let series of seriesList) { + let bound = Number(series.alias); + if (isNaN(bound)) { return; } - let values = new Array(Math.round(count)); - values.fill(Number(bound)); + for (let point of series.datapoints) { + let count = point[VALUE_INDEX]; + let time = point[TIME_INDEX]; - let valueBuckets = {}; - valueBuckets[bound] = { - y: Number(bound), - values: values - }; + if (!_.isNumber(count)) { + continue; + } - let xBucket: XBucket = { - x: point[TIME_INDEX], - buckets: valueBuckets - }; + let bucket = heatmap[time]; + if (!bucket) { + bucket = heatmap[time] = {x: time, buckets: {}}; + } - // Don't push buckets with 0 count until saveZeroCounts flag is set - if (count !== 0 || (count === 0 && saveZeroCounts)) { - xBuckets.push(xBucket); + bucket.buckets[bound] = {y: bound, count: count}; } - }); - - let heatmap: any = {}; - _.forEach(xBuckets, (bucket: XBucket) => { - heatmap[bucket.x] = bucket; - }); + } return heatmap; } -/** - * Convert set of time series into heatmap buckets - * @return {Object} Heatmap object: + /** + * Convert set of time series into heatmap buckets + * @return {Object} Heatmap object: * { * xBucketBound_1: { * x: xBucketBound_1, @@ -109,18 +91,16 @@ function convertToHeatMap(series, yBucketSize, xBucketSize, logBase) { function convertToCards(buckets) { let cards = []; _.forEach(buckets, xBucket => { - _.forEach(xBucket.buckets, (yBucket, key) => { - if (yBucket.values.length) { - let card = { - x: Number(xBucket.x), - y: Number(key), - yBounds: yBucket.bounds, - values: yBucket.values, - seriesStat: getSeriesStat(yBucket.points) - }; - - cards.push(card); - } + _.forEach(xBucket.buckets, yBucket=> { + let card = { + x: xBucket.x, + y: yBucket.y, + yBounds: yBucket.bounds, + values: yBucket.values, + count: yBucket.count, + seriesStat: getSeriesStat(yBucket.points) + }; + cards.push(card); }); }); diff --git a/public/app/plugins/panel/heatmap/partials/axes_editor.html b/public/app/plugins/panel/heatmap/partials/axes_editor.html index 5841470f69c..b60e21eab85 100644 --- a/public/app/plugins/panel/heatmap/partials/axes_editor.html +++ b/public/app/plugins/panel/heatmap/partials/axes_editor.html @@ -2,7 +2,7 @@
    Y Axis
    - +
    - +
    - +
    - +
    - + @@ -55,7 +55,7 @@ + ng-model="ctrl.panel.xBucketNumber" ng-change="ctrl.refresh()" ng-model-onblur>
    diff --git a/public/app/plugins/panel/heatmap/rendering.ts b/public/app/plugins/panel/heatmap/rendering.ts index b822de2b068..12e7926f76f 100644 --- a/public/app/plugins/panel/heatmap/rendering.ts +++ b/public/app/plugins/panel/heatmap/rendering.ts @@ -366,14 +366,12 @@ export default function link(scope, elem, attrs, ctrl) { data.buckets = mergeZeroBuckets(data.buckets, _.min(tick_values)); } } + let cardsData = convertToCards(data.buckets); + let maxValue = d3.max(cardsData, card => card.count); - let max_value = d3.max(cardsData, card => { - return card.values.length; - }); - - colorScale = getColorScale(max_value); - setOpacityScale(max_value); + colorScale = getColorScale(maxValue); + setOpacityScale(maxValue); setCardSize(); let cards = heatmap.selectAll(".heatmap-card").data(cardsData); @@ -431,14 +429,14 @@ export default function link(scope, elem, attrs, ctrl) { return d3.scaleSequential(colorInterpolator).domain([start, end]); } - function setOpacityScale(max_value) { + function setOpacityScale(maxValue) { if (panel.color.colorScale === 'linear') { opacityScale = d3.scaleLinear() - .domain([0, max_value]) + .domain([0, maxValue]) .range([0, 1]); } else if (panel.color.colorScale === 'sqrt') { opacityScale = d3.scalePow().exponent(panel.color.exponent) - .domain([0, max_value]) + .domain([0, maxValue]) .range([0, 1]); } } @@ -529,13 +527,13 @@ export default function link(scope, elem, attrs, ctrl) { if (panel.color.mode === 'opacity') { return panel.color.cardColor; } else { - return colorScale(d.values.length); + return colorScale(d.count); } } function getCardOpacity(d) { if (panel.color.mode === 'opacity') { - return opacityScale(d.values.length); + return opacityScale(d.count); } else { return 1; } @@ -831,8 +829,3 @@ function getPrecision(num) { return str.length - dot_index - 1; } } - -function getTicksPrecision(values) { - let precisions = _.map(values, getPrecision); - return _.max(precisions); -} diff --git a/public/app/plugins/panel/heatmap/specs/heatmap_data_converter_specs.ts b/public/app/plugins/panel/heatmap/specs/heatmap_data_converter_specs.ts index b1019b94c56..0c9534700d4 100644 --- a/public/app/plugins/panel/heatmap/specs/heatmap_data_converter_specs.ts +++ b/public/app/plugins/panel/heatmap/specs/heatmap_data_converter_specs.ts @@ -200,7 +200,7 @@ describe('ES Histogram converter', () => { alias: '1', label: '1' })); ctx.series.push(new TimeSeries({ - datapoints: [[1, 1422774000000], [3, 1422774060000]], + datapoints: [[5, 1422774000000], [3, 1422774060000]], alias: '2', label: '2' })); ctx.series.push(new TimeSeries({ @@ -219,21 +219,23 @@ describe('ES Histogram converter', () => { '1422774000000': { x: 1422774000000, buckets: { - '1': { y: 1, values: [1] }, - '2': { y: 2, values: [2] } + '1': { y: 1, count: 1 }, + '2': { y: 2, count: 5 }, + '3': { y: 3, count: 0 } } }, '1422774060000': { x: 1422774060000, buckets: { - '2': { y: 2, values: [2, 2, 2] }, - '3': { y: 3, values: [3] } + '1': { y: 1, count: 0 }, + '2': { y: 2, count: 3 }, + '3': { y: 3, count: 1 } } }, }; let heatmap = elasticHistogramToHeatmap(ctx.series); - expect(isHeatmapDataEqual(heatmap, expectedHeatmap)).to.be(true); + expect(heatmap).to.eql(expectedHeatmap); }); }); }); From ece21b2d95b604e19334e92024961d4ffe5af2bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Fri, 5 May 2017 17:28:33 +0200 Subject: [PATCH 38/85] heatmap: more refactoring --- .../panel/heatmap/heatmap_data_converter.ts | 206 +++++++----------- .../plugins/panel/heatmap/heatmap_tooltip.ts | 2 +- .../specs/heatmap_data_converter_specs.ts | 22 +- 3 files changed, 90 insertions(+), 140 deletions(-) diff --git a/public/app/plugins/panel/heatmap/heatmap_data_converter.ts b/public/app/plugins/panel/heatmap/heatmap_data_converter.ts index 28488c1faf8..2115d4070be 100644 --- a/public/app/plugins/panel/heatmap/heatmap_data_converter.ts +++ b/public/app/plugins/panel/heatmap/heatmap_data_converter.ts @@ -45,44 +45,6 @@ function elasticHistogramToHeatmap(seriesList) { return heatmap; } - /** - * Convert set of time series into heatmap buckets - * @return {Object} Heatmap object: - * { - * xBucketBound_1: { - * x: xBucketBound_1, - * buckets: { - * yBucketBound_1: { - * y: yBucketBound_1, - * bounds: {bottom, top} - * values: [val_1, val_2, ..., val_K], - * points: [[val_Y, val_X, series_name], ..., [...]], - * seriesStat: {seriesName_1: val_1, seriesName_2: val_2} - * }, - * ... - * yBucketBound_M: {} - * }, - * values: [val_1, val_2, ..., val_K], - * points: [ - * [val_Y, val_X, series_name], (point_1) - * ... - * [...] (point_K) - * ] - * }, - * xBucketBound_2: {}, - * ... - * xBucketBound_N: {} - * } - */ -function convertToHeatMap(series, yBucketSize, xBucketSize, logBase) { - let seriesBuckets = _.map(series, s => { - return seriesToHeatMap(s, yBucketSize, xBucketSize, logBase); - }); - - let buckets = mergeBuckets(seriesBuckets); - return buckets; -} - /** * Convert buckets into linear array of "cards" - objects, represented heatmap elements. * @param {Object} buckets @@ -193,23 +155,52 @@ function getSeriesStat(points) { } /** - * Convert individual series to heatmap buckets + * Convert set of time series into heatmap buckets + * @return {Object} Heatmap object: + * { + * xBucketBound_1: { + * x: xBucketBound_1, + * buckets: { + * yBucketBound_1: { + * y: yBucketBound_1, + * bounds: {bottom, top} + * values: [val_1, val_2, ..., val_K], + * points: [[val_Y, val_X, series_name], ..., [...]], + * seriesStat: {seriesName_1: val_1, seriesName_2: val_2} + * }, + * ... + * yBucketBound_M: {} + * }, + * values: [val_1, val_2, ..., val_K], + * points: [ + * [val_Y, val_X, series_name], (point_1) + * ... + * [...] (point_K) + * ] + * }, + * xBucketBound_2: {}, + * ... + * xBucketBound_N: {} + * } */ -function seriesToHeatMap(series, yBucketSize, xBucketSize, logBase = 1) { - let datapoints = series.datapoints; - let seriesName = series.label; - let xBuckets = {}; +function convertToHeatMap(seriesList, yBucketSize, xBucketSize, logBase = 1) { + let heatmap = {}; - // Slice series into X axis buckets - // | | ** | | * | **| - // | * |* *|* |** *| * | - // |** *| | ***| |* | - // |____|____|____|____|____|_ - // - _.forEach(datapoints, point => { - let bucketBound = getBucketBound(point[TIME_INDEX], xBucketSize); - pushToXBuckets(xBuckets, point, bucketBound, seriesName); - }); + for (let series of seriesList) { + let datapoints = series.datapoints; + let seriesName = series.label; + + // Slice series into X axis buckets + // | | ** | | * | **| + // | * |* *|* |** *| * | + // |** *| | ***| |* | + // |____|____|____|____|____|_ + // + _.forEach(datapoints, point => { + let bucketBound = getBucketBound(point[TIME_INDEX], xBucketSize); + pushToXBuckets(heatmap, point, bucketBound, seriesName); + }); + } // Slice X axis buckets into Y (value) buckets // | **| |2|, @@ -217,14 +208,15 @@ function seriesToHeatMap(series, yBucketSize, xBucketSize, logBase = 1) { // |* | --/ |1|, // |____| |0| // - _.forEach(xBuckets, xBucket => { + _.forEach(heatmap, xBucket => { if (logBase !== 1) { xBucket.buckets = convertToLogScaleValueBuckets(xBucket, yBucketSize, logBase); } else { xBucket.buckets = convertToValueBuckets(xBucket, yBucketSize); } }); - return xBuckets; + + return heatmap; } function pushToXBuckets(buckets, point, bucketNum, seriesName) { @@ -249,13 +241,13 @@ function pushToXBuckets(buckets, point, bucketNum, seriesName) { function pushToYBuckets(buckets, bucketNum, value, point, bounds) { if (buckets[bucketNum]) { buckets[bucketNum].values.push(value); - buckets[bucketNum].points.push(point); + buckets[bucketNum].count += 1; } else { buckets[bucketNum] = { y: bucketNum, bounds: bounds, values: [value], - points: [point] + count: 1, }; } } @@ -288,6 +280,7 @@ function convertToValueBuckets(xBucket, bucketSize) { let values = xBucket.values; let points = xBucket.points; let buckets = {}; + _.forEach(values, (val, index) => { let bounds = getBucketBounds(val, bucketSize); let bucketNum = bounds.bottom; @@ -343,53 +336,6 @@ function convertToLogScaleValueBuckets(xBucket, yBucketSplitFactor, logBase) { return buckets; } -/** - * Merge individual buckets for all series into one - * @param {Array} seriesBuckets Array of series buckets - * @return {Object} Merged buckets. - */ -function mergeBuckets(seriesBuckets) { - let mergedBuckets: any = {}; - _.forEach(seriesBuckets, (seriesBucket, index) => { - if (index === 0) { - mergedBuckets = seriesBucket; - } else { - _.forEach(seriesBucket, (xBucket, xBound) => { - if (mergedBuckets[xBound]) { - if (xBucket.points) { - mergedBuckets[xBound].points = xBucket.points.concat(mergedBuckets[xBound].points); - } - if (xBucket.values) { - mergedBuckets[xBound].values = xBucket.values.concat(mergedBuckets[xBound].values); - } - - _.forEach(xBucket.buckets, (yBucket, yBound) => { - let bucket = mergedBuckets[xBound].buckets[yBound]; - if (bucket && bucket.values) { - mergedBuckets[xBound].buckets[yBound].values = bucket.values.concat(yBucket.values); - - if (bucket.points) { - mergedBuckets[xBound].buckets[yBound].points = bucket.points.concat(yBucket.points); - } - } else { - mergedBuckets[xBound].buckets[yBound] = yBucket; - } - - let points = mergedBuckets[xBound].buckets[yBound].points; - if (points) { - mergedBuckets[xBound].buckets[yBound].seriesStat = getSeriesStat(points); - } - }); - } else { - mergedBuckets[xBound] = xBucket; - } - }); - } - }); - - return mergedBuckets; -} - // Get minimum non zero value. function getMinLog(series) { let values = _.compact(_.map(series.datapoints, p => p[0])); @@ -454,36 +400,36 @@ function isHeatmapDataEqual(objA: any, objB: any): boolean { let is_eql = !emptyXOR(objA, objB); _.forEach(objA, (xBucket: XBucket, x) => { - if (objB[x]) { + if (objB[x]) { if (emptyXOR(xBucket.buckets, objB[x].buckets)) { - is_eql = false; - return false; + is_eql = false; + return false; } _.forEach(xBucket.buckets, (yBucket: YBucket, y) => { - if (objB[x].buckets && objB[x].buckets[y]) { + if (objB[x].buckets && objB[x].buckets[y]) { if (objB[x].buckets[y].values) { - is_eql = _.isEqual(_.sortBy(yBucket.values), _.sortBy(objB[x].buckets[y].values)); - if (!is_eql) { - return false; - } - } else { - is_eql = false; - return false; + is_eql = _.isEqual(_.sortBy(yBucket.values), _.sortBy(objB[x].buckets[y].values)); + if (!is_eql) { + return false; } - } else { + } else { is_eql = false; return false; - } - }); + } + } else { + is_eql = false; + return false; + } + }); if (!is_eql) { return false; } - } else { - is_eql = false; - return false; - } + } else { + is_eql = false; + return false; + } }); return is_eql; @@ -495,12 +441,12 @@ function emptyXOR(foo: any, bar: any): boolean { export { convertToHeatMap, - elasticHistogramToHeatmap, - convertToCards, - removeZeroBuckets, - mergeZeroBuckets, - getMinLog, - getValueBucketBound, - isHeatmapDataEqual, - calculateBucketSize + elasticHistogramToHeatmap, + convertToCards, + removeZeroBuckets, + mergeZeroBuckets, + getMinLog, + getValueBucketBound, + isHeatmapDataEqual, + calculateBucketSize }; diff --git a/public/app/plugins/panel/heatmap/heatmap_tooltip.ts b/public/app/plugins/panel/heatmap/heatmap_tooltip.ts index fe36112b859..71207886d7d 100644 --- a/public/app/plugins/panel/heatmap/heatmap_tooltip.ts +++ b/public/app/plugins/panel/heatmap/heatmap_tooltip.ts @@ -90,7 +90,7 @@ export class HeatmapTooltip { if (yData && yData.bounds) { boundBottom = valueFormatter(yData.bounds.bottom); boundTop = valueFormatter(yData.bounds.top); - valuesNumber = yData.values.length; + valuesNumber = yData.count; tooltipHtml += `
    bucket: ${boundBottom} - ${boundTop}
    count: ${valuesNumber}
    diff --git a/public/app/plugins/panel/heatmap/specs/heatmap_data_converter_specs.ts b/public/app/plugins/panel/heatmap/specs/heatmap_data_converter_specs.ts index 0c9534700d4..7c8dce2b822 100644 --- a/public/app/plugins/panel/heatmap/specs/heatmap_data_converter_specs.ts +++ b/public/app/plugins/panel/heatmap/specs/heatmap_data_converter_specs.ts @@ -119,21 +119,24 @@ describe('HeatmapDataConverter', () => { beforeEach(() => { ctx.series = []; ctx.series.push(new TimeSeries({ - datapoints: [[1, 1422774000000], [2, 1422774060000]], + datapoints: [[1, 1422774000000], [1, 1422774000010], [2, 1422774060000]], alias: 'series1' })); ctx.series.push(new TimeSeries({ - datapoints: [[2, 1422774000000], [3, 1422774060000]], + datapoints: [[2, 1422774000000], [2, 1422774000010], [3, 1422774060000]], alias: 'series2' })); + ctx.series.push(new TimeSeries({ + datapoints: [[5, 1422774000000], [3, 1422774000010], [4, 1422774060000]], + alias: 'series3' + })); ctx.xBucketSize = 60000; // 60s - ctx.yBucketSize = 1; + ctx.yBucketSize = 2; ctx.logBase = 1; }); describe('when logBase is 1 (linear scale)', () => { - beforeEach(() => { ctx.logBase = 1; }); @@ -143,15 +146,16 @@ describe('HeatmapDataConverter', () => { '1422774000000': { x: 1422774000000, buckets: { - '1': { y: 1, values: [1] }, - '2': { y: 2, values: [2] } + '0': {y: 0, values: [1, 1], count: 2, bounds: {bottom: 0, top: 2}}, + '2': {y: 2, values: [2, 2, 3], count: 3, bounds: {bottom: 2, top: 4}}, + '4': {y: 4, values: [5], count: 1, bounds: {bottom: 4, top: 6}}, } }, '1422774060000': { x: 1422774060000, buckets: { - '2': { y: 2, values: [2] }, - '3': { y: 3, values: [3] } + '2': {y: 2, values: [2, 3], count: 3, bounds: {bottom: 2, top: 4}}, + '4': {y: 4, values: [4], count: 1, bounds: {bottom: 4, top: 6}}, } }, }; @@ -161,7 +165,7 @@ describe('HeatmapDataConverter', () => { }); }); - describe('when logBase is 2', () => { + describe.skip('when logBase is 2', () => { beforeEach(() => { ctx.logBase = 2; From fbf39598b855dea9959dae9e6554be1bba1d489a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Fri, 5 May 2017 17:33:54 +0200 Subject: [PATCH 39/85] heatmp: removed series stats option, lacked tests --- .../app/plugins/panel/heatmap/heatmap_ctrl.ts | 1 - .../panel/heatmap/heatmap_data_converter.ts | 10 ---- .../plugins/panel/heatmap/heatmap_tooltip.ts | 58 ++++++++----------- .../heatmap/partials/display_editor.html | 4 -- 4 files changed, 25 insertions(+), 48 deletions(-) diff --git a/public/app/plugins/panel/heatmap/heatmap_ctrl.ts b/public/app/plugins/panel/heatmap/heatmap_ctrl.ts index 5600a5ea2e4..02d2ef24b79 100644 --- a/public/app/plugins/panel/heatmap/heatmap_ctrl.ts +++ b/public/app/plugins/panel/heatmap/heatmap_ctrl.ts @@ -46,7 +46,6 @@ let panelDefaults = { yBucketNumber: null, tooltip: { show: true, - seriesStat: false, showHistogram: false }, highlightCards: true diff --git a/public/app/plugins/panel/heatmap/heatmap_data_converter.ts b/public/app/plugins/panel/heatmap/heatmap_data_converter.ts index 2115d4070be..f627173d233 100644 --- a/public/app/plugins/panel/heatmap/heatmap_data_converter.ts +++ b/public/app/plugins/panel/heatmap/heatmap_data_converter.ts @@ -60,7 +60,6 @@ function convertToCards(buckets) { yBounds: yBucket.bounds, values: yBucket.values, count: yBucket.count, - seriesStat: getSeriesStat(yBucket.points) }; cards.push(card); }); @@ -145,15 +144,6 @@ function removeZeroBuckets(buckets) { return buckets; } -/** - * Count values number for each timeseries in given bucket - * @param {Array} points Bucket's datapoints with series name ([val, ts, series_name]) - * @return {Object} seriesStat: {seriesName_1: val_1, seriesName_2: val_2} - */ -function getSeriesStat(points) { - return _.countBy(points, p => p[2]); -} - /** * Convert set of time series into heatmap buckets * @return {Object} Heatmap object: diff --git a/public/app/plugins/panel/heatmap/heatmap_tooltip.ts b/public/app/plugins/panel/heatmap/heatmap_tooltip.ts index 71207886d7d..28824d7c7e2 100644 --- a/public/app/plugins/panel/heatmap/heatmap_tooltip.ts +++ b/public/app/plugins/panel/heatmap/heatmap_tooltip.ts @@ -87,17 +87,18 @@ export class HeatmapTooltip { let tooltipHtml = `
    ${time}
    `; - if (yData && yData.bounds) { - boundBottom = valueFormatter(yData.bounds.bottom); - boundTop = valueFormatter(yData.bounds.top); - valuesNumber = yData.count; - tooltipHtml += `
    - bucket: ${boundBottom} - ${boundTop}
    - count: ${valuesNumber}
    -
    `; - - if (this.panel.tooltip.seriesStat && yData.seriesStat) { - tooltipHtml = this.addSeriesStat(tooltipHtml, yData.seriesStat); + if (yData) { + if (yData.bounds) { + boundBottom = valueFormatter(yData.bounds.bottom); + boundTop = valueFormatter(yData.bounds.top); + valuesNumber = yData.count; + tooltipHtml += `
    + bucket: ${boundBottom} - ${boundTop}
    + count: ${valuesNumber}
    +
    `; + } else { + // currently no bounds for pre bucketed data + tooltipHtml += `
    count: ${yData.count}
    `; } } else { if (!this.panel.tooltip.showHistogram) { @@ -159,15 +160,6 @@ export class HeatmapTooltip { return pos; } - addSeriesStat(tooltipHtml, seriesStat) { - tooltipHtml += "series:
    "; - _.forEach(seriesStat, (values, series) => { - tooltipHtml += `  -  ${series}: ${values}
    `; - }); - - return tooltipHtml; - } - addHistogram(data) { let xBucket = this.scope.ctrl.data.buckets[data.x]; let yBucketSize = this.scope.ctrl.data.yBucketSize; @@ -181,8 +173,8 @@ export class HeatmapTooltip { let scale = this.scope.yScale.copy(); let histXScale = scale - .domain([min, max]) - .range([0, HISTOGRAM_WIDTH]); + .domain([min, max]) + .range([0, HISTOGRAM_WIDTH]); let barWidth; if (this.panel.yAxis.logBase === 1) { @@ -193,21 +185,21 @@ export class HeatmapTooltip { barWidth = Math.max(barWidth, 1); let histYScale = d3.scaleLinear() - .domain([0, _.max(_.map(histogramData, d => d[1]))]) - .range([0, HISTOGRAM_HEIGHT]); + .domain([0, _.max(_.map(histogramData, d => d[1]))]) + .range([0, HISTOGRAM_HEIGHT]); let histogram = this.tooltip.select(".heatmap-histogram") - .append("svg") - .attr("width", HISTOGRAM_WIDTH) - .attr("height", HISTOGRAM_HEIGHT); + .append("svg") + .attr("width", HISTOGRAM_WIDTH) + .attr("height", HISTOGRAM_HEIGHT); histogram.selectAll(".bar").data(histogramData) - .enter().append("rect") - .attr("x", d => { - return histXScale(d[0]); - }) - .attr("width", barWidth) - .attr("y", d => { + .enter().append("rect") + .attr("x", d => { + return histXScale(d[0]); + }) + .attr("width", barWidth) + .attr("y", d => { return HISTOGRAM_HEIGHT - histYScale(d[1]); }) .attr("height", d => { diff --git a/public/app/plugins/panel/heatmap/partials/display_editor.html b/public/app/plugins/panel/heatmap/partials/display_editor.html index a6d1de1981c..863fcc49d07 100644 --- a/public/app/plugins/panel/heatmap/partials/display_editor.html +++ b/public/app/plugins/panel/heatmap/partials/display_editor.html @@ -62,10 +62,6 @@ checked="ctrl.panel.tooltip.show" on-change="ctrl.render()">
    - - From 29653d2becfb677cfeecb7563c8bca36ac11c225 Mon Sep 17 00:00:00 2001 From: Dan Cech Date: Fri, 5 May 2017 12:40:49 -0400 Subject: [PATCH 40/85] refactor and add column alias tests --- public/app/core/utils/file_export.ts | 2 +- public/app/plugins/panel/table/module.html | 2 +- public/app/plugins/panel/table/module.ts | 25 ++------ public/app/plugins/panel/table/renderer.ts | 61 +++++++++++-------- .../panel/table/specs/renderer_specs.ts | 18 +++++- .../app/plugins/panel/table/transformers.ts | 3 +- 6 files changed, 61 insertions(+), 50 deletions(-) diff --git a/public/app/core/utils/file_export.ts b/public/app/core/utils/file_export.ts index f2f0192e034..24b501eee28 100644 --- a/public/app/core/utils/file_export.ts +++ b/public/app/core/utils/file_export.ts @@ -53,7 +53,7 @@ export function exportTableDataToCsv(table) { var text = 'sep=;\n'; // add header _.each(table.columns, function(column) { - text += column.text + ';'; + text += (column.title || column.text) + ';'; }); text += '\n'; // process data diff --git a/public/app/plugins/panel/table/module.html b/public/app/plugins/panel/table/module.html index 226fe095ca3..5c6fcbfdb1e 100644 --- a/public/app/plugins/panel/table/module.html +++ b/public/app/plugins/panel/table/module.html @@ -7,7 +7,7 @@
    - {{col.title || col.text}} + {{col.title}} diff --git a/public/app/plugins/panel/table/module.ts b/public/app/plugins/panel/table/module.ts index 155934dc85e..e8e3cae1a6b 100644 --- a/public/app/plugins/panel/table/module.ts +++ b/public/app/plugins/panel/table/module.ts @@ -17,6 +17,7 @@ class TablePanelCtrl extends MetricsPanelCtrl { pageIndex: number; dataRaw: any; table: any; + renderer: any; panelDefaults = { targets: [{}], @@ -122,22 +123,7 @@ class TablePanelCtrl extends MetricsPanelCtrl { this.table = transformDataToTable(this.dataRaw, this.panel); this.table.sort(this.panel.sort); - for (let colIndex = 0; colIndex < this.table.columns.length; colIndex++) { - let column = this.table.columns[colIndex]; - - for (let i = 0; i < this.panel.styles.length; i++) { - let style = this.panel.styles[i]; - var regex = kbn.stringToJsRegex(style.pattern); - const matches = column.text.match(regex); - if (matches) { - column.style = style; - if (style.alias) { - column.title = column.text.replace(regex, style.alias); - } - break; - } - } - } + this.renderer = new TableRenderer(this.panel, this.table, this.dashboard.isTimezoneUtc(), this.$sanitize); return super.render(this.table); } @@ -162,8 +148,7 @@ class TablePanelCtrl extends MetricsPanelCtrl { } exportCsv() { - var renderer = new TableRenderer(this.panel, this.table, this.dashboard.isTimezoneUtc(), this.$sanitize); - FileExport.exportTableDataToCsv(renderer.render_values()); + FileExport.exportTableDataToCsv(this.renderer.render_values()); } link(scope, elem, attrs, ctrl) { @@ -183,9 +168,9 @@ class TablePanelCtrl extends MetricsPanelCtrl { } function appendTableRows(tbodyElem) { - var renderer = new TableRenderer(panel, data, ctrl.dashboard.isTimezoneUtc(), ctrl.$sanitize); + ctrl.renderer.setTable(data); tbodyElem.empty(); - tbodyElem.html(renderer.render(ctrl.pageIndex)); + tbodyElem.html(ctrl.renderer.render(ctrl.pageIndex)); } function switchPage(e) { diff --git a/public/app/plugins/panel/table/renderer.ts b/public/app/plugins/panel/table/renderer.ts index f51af0a0de4..ae56e8dff12 100644 --- a/public/app/plugins/panel/table/renderer.ts +++ b/public/app/plugins/panel/table/renderer.ts @@ -5,12 +5,44 @@ import moment from 'moment'; import kbn from 'app/core/utils/kbn'; export class TableRenderer { - formaters: any[]; + formatters: any[]; colorState: any; constructor(private panel, private table, private isUtc, private sanitize) { - this.formaters = []; + this.initColumns(); + } + + setTable(table) { + this.table = table; + + this.initColumns(); + } + + initColumns() { + this.formatters = []; this.colorState = {}; + + for (let colIndex = 0; colIndex < this.table.columns.length; colIndex++) { + let column = this.table.columns[colIndex]; + column.title = column.text; + + for (let i = 0; i < this.panel.styles.length; i++) { + let style = this.panel.styles[i]; + + var regex = kbn.stringToJsRegex(style.pattern); + if (column.text.match(regex)) { + column.style = style; + + if (style.alias) { + column.title = column.text.replace(regex, style.alias); + } + + break; + } + } + + this.formatters[colIndex] = this.createColumnFormatter(column); + } } getColorForValue(value, style) { @@ -92,28 +124,7 @@ export class TableRenderer { } formatColumnValue(colIndex, value) { - if (!this.formaters[colIndex]) { - let column = this.table.columns[colIndex]; - - if (!column.style) { - for (let i = 0; i < this.panel.styles.length; i++) { - let style = this.panel.styles[i]; - var regex = kbn.stringToJsRegex(style.pattern); - const matches = column.text.match(regex); - if (matches) { - column.style = style; - if (style.alias) { - column.title = column.text.replace(regex, style.alias); - } - break; - } - } - } - - this.formaters[colIndex] = this.createColumnFormatter(column); - } - - return this.formaters[colIndex](value); + return this.formatters[colIndex] ? this.formatters[colIndex](value) : value; } renderCell(columnIndex, value, addWidthHack = false) { @@ -132,7 +143,7 @@ export class TableRenderer { // this hack adds header content to cell (not visible) var widthHack = ''; if (addWidthHack) { - widthHack = '
    ' + this.table.columns[columnIndex].text + '
    '; + widthHack = '
    ' + this.table.columns[columnIndex].title + '
    '; } if (value === undefined) { diff --git a/public/app/plugins/panel/table/specs/renderer_specs.ts b/public/app/plugins/panel/table/specs/renderer_specs.ts index 46789f295d1..6b031d4bb91 100644 --- a/public/app/plugins/panel/table/specs/renderer_specs.ts +++ b/public/app/plugins/panel/table/specs/renderer_specs.ts @@ -22,13 +22,15 @@ describe('when rendering table', () => { { pattern: 'Time', type: 'date', - format: 'LLL' + format: 'LLL', + alias: 'Timestamp' }, { - pattern: 'Value', + pattern: '/(Val)ue/', type: 'number', unit: 'ms', decimals: 3, + alias: '$1' }, { pattern: 'Colored', @@ -132,6 +134,18 @@ describe('when rendering table', () => { var html = renderer.renderCell(6, 'text link'); expect(html).to.be('sanitized'); }); + + it('Time column title should be Timestamp', () => { + expect(table.columns[0].title).to.be('Timestamp'); + }); + + it('Value column title should be Val', () => { + expect(table.columns[1].title).to.be('Val'); + }); + + it('Colored column title should be Colored', () => { + expect(table.columns[2].title).to.be('Colored'); + }); }); }); diff --git a/public/app/plugins/panel/table/transformers.ts b/public/app/plugins/panel/table/transformers.ts index 15cc9e71134..0f793fa0434 100644 --- a/public/app/plugins/panel/table/transformers.ts +++ b/public/app/plugins/panel/table/transformers.ts @@ -229,7 +229,7 @@ function transformDataToTable(data, panel) { var transformer = transformers[panel.transform]; if (!transformer) { - throw {message: 'Transformer ' + panel.transformer + ' not found'}; + throw {message: 'Transformer ' + panel.transform + ' not found'}; } if (panel.filterNull) { @@ -239,6 +239,7 @@ function transformDataToTable(data, panel) { } transformer.transform(copyData, panel, model); + return model; } From e8fbfce59a7a32b38d121a7dd9fd045071987e9d Mon Sep 17 00:00:00 2001 From: Dan Cech Date: Fri, 5 May 2017 13:11:44 -0400 Subject: [PATCH 41/85] remove unneeded import, update docs text --- docs/sources/features/panels/table_panel.md | 9 +++++---- public/app/plugins/panel/table/module.ts | 1 - 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/sources/features/panels/table_panel.md b/docs/sources/features/panels/table_panel.md index 9ddc2cddaee..69cd02fdbcc 100644 --- a/docs/sources/features/panels/table_panel.md +++ b/docs/sources/features/panels/table_panel.md @@ -85,8 +85,9 @@ The column styles allow you control how dates and numbers are formatted. 1. `Name or regex`: The Name or Regex field controls what columns the rule should be applied to. The regex or name filter will be matched against the column name not against column values. 2. `Type`: The three supported types of types are `Number`, `String` and `Date`. -3. `Format`: Specify date format. Only available when `Type` is set to `Date`. -4. `Coloring` and `Thresholds`: Specify color mode and thresholds limits. -5. `Unit` and `Decimals`: Specify unit and decimal precision for numbers. -6. `Add column style rule`: Add new column rule. +3. `Title`: Title for the column, when using a Regex the title can include replacement strings like `$1`. +4. `Format`: Specify date format. Only available when `Type` is set to `Date`. +5. `Coloring` and `Thresholds`: Specify color mode and thresholds limits. +6. `Unit` and `Decimals`: Specify unit and decimal precision for numbers. +7. `Add column style rule`: Add new column rule. diff --git a/public/app/plugins/panel/table/module.ts b/public/app/plugins/panel/table/module.ts index e8e3cae1a6b..9b563ca5b18 100644 --- a/public/app/plugins/panel/table/module.ts +++ b/public/app/plugins/panel/table/module.ts @@ -9,7 +9,6 @@ import {MetricsPanelCtrl} from 'app/plugins/sdk'; import {transformDataToTable} from './transformers'; import {tablePanelEditor} from './editor'; import {TableRenderer} from './renderer'; -import kbn from 'app/core/utils/kbn'; class TablePanelCtrl extends MetricsPanelCtrl { static templateUrl = 'module.html'; From c323d2fc4cd89e3e59d9f821eb00884b590d541f Mon Sep 17 00:00:00 2001 From: lpavlovi Date: Sun, 7 May 2017 02:19:08 -0400 Subject: [PATCH 42/85] Removed panelElemName - appears to not be used anywhere (#8313) --- public/app/core/directives/plugin_component.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/public/app/core/directives/plugin_component.ts b/public/app/core/directives/plugin_component.ts index 3e446b14b9e..4c098f60a4c 100644 --- a/public/app/core/directives/plugin_component.ts +++ b/public/app/core/directives/plugin_component.ts @@ -61,7 +61,6 @@ function pluginDirectiveLoader($compile, datasourceSrv, $rootScope, $q, $http, $ attrs: {dashboard: "ctrl.dashboard", panel: "panel", row: "ctrl.row"}, }; - var panelElemName = 'panel-' + scope.panel.type; let panelInfo = config.panels[scope.panel.type]; var panelCtrlPromise = Promise.resolve(UnknownPanelCtrl); if (panelInfo) { From e2f0b42d902a61fc936e8ecc8d4be0da5cac708e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Mon, 8 May 2017 06:49:44 +0200 Subject: [PATCH 43/85] docs: updated cloudwatch docs, closes #8315 --- .../features/datasources/cloudwatch.md | 84 +++++++++++-------- 1 file changed, 48 insertions(+), 36 deletions(-) diff --git a/docs/sources/features/datasources/cloudwatch.md b/docs/sources/features/datasources/cloudwatch.md index c2896c5b172..8d77e5c59c0 100644 --- a/docs/sources/features/datasources/cloudwatch.md +++ b/docs/sources/features/datasources/cloudwatch.md @@ -13,29 +13,26 @@ weight = 10 # Using AWS CloudWatch in Grafana -Grafana ships with built in support for CloudWatch. You just have to add it as a data source and you will -be ready to build dashboards for you CloudWatch metrics. +Grafana ships with built in support for CloudWatch. You just have to add it as a data source and you will be ready to build dashboards for you CloudWatch metrics. -## Adding the data source -![](/img/docs/cloudwatch/cloudwatch_add.png) +## Adding the data source to Grafana -1. Open the side menu by clicking the the Grafana icon in the top header. +1. Open the side menu by clicking the Grafana icon in the top header. 2. In the side menu under the `Dashboards` link you should find a link named `Data Sources`. +3. Click the `+ Add data source` button in the top header. +4. Select `Cloudwatch` from the *Type* dropdown. - > NOTE: If this link is missing in the side menu it means that your current user does not have the `Admin` role for the current organization. - -3. Click the `Add new` link in the top header. -4. Select `CloudWatch` from the dropdown. - > NOTE: If at any moment you have issues with getting this datasource to work and grafana is giving you undescriptive errors then dont forget to check your log file (try looking in /var/log/grafana/). +> NOTE: If at any moment you have issues with getting this datasource to work and Grafana is giving you undescriptive errors then don't +forget to check your log file (try looking in /var/log/grafana/grafana.log). Name | Description ------------ | ------------- -Name | The data source name, important that this is the same as in Grafana v1.x if you plan to import old dashboards. -Default | Default data source means that it will be pre-selected for new panels. -Credentials profile name | Specify the name of the profile to use (if you use `~/aws/credentials` file), leave blank for default. This option was introduced in Grafana 2.5.1 -Default Region | Used in query editor to set region (can be changed on per query basis) -Custom Metrics namespace | Specify the CloudWatch namespace of Custom metrics -Assume Role Arn | Specify the ARN of the role to assume +*Name* | The data source name. This is how you refer to the data source in panels & queries. +*Default* | Default data source means that it will be pre-selected for new panels. +*Credentials* profile name | Specify the name of the profile to use (if you use `~/aws/credentials` file), leave blank for default. +*Default Region* | Used in query editor to set region (can be changed on per query basis) +*Custom Metrics namespace* | Specify the CloudWatch namespace of Custom metrics +*Assume Role Arn* | Specify the ARN of the role to assume ## Authentication @@ -61,49 +58,64 @@ Example content: ## Metric Query Editor -![](/img/docs/cloudwatch/query_editor.png) +![](/img/docs/v43/cloudwatch_editor.png) You need to specify a namespace, metric, at least one stat, and at least one dimension. ## Templated queries -CloudWatch Datasource Plugin provides the following functions in `Variables values query` field in Templating Editor to query `region`, `namespaces`, `metric names` and `dimension keys/values` on the CloudWatch. + +Instead of hard-coding things like server, application and sensor name in you metric queries you can use variables in their place. +Variables are shown as dropdown select boxes at the top of the dashboard. These dropdowns makes it easy to change the data +being displayed in your dashboard. + +Checkout the [Templating]({{< relref "reference/templating.md" >}}) documentation for an introduction to the templating feature and the different +types of template variables. + +### Query variable + +CloudWatch Datasource Plugin provides the following queries you can specify in the `Query` field in the Variable +edit view. They allow you to fill a variable's options list with things like `region`, `namespaces`, `metric names` +and `dimension keys/values`. Name | Description ------- | -------- -`regions()` | Returns a list of regions AWS provides their service. -`namespaces()` | Returns a list of namespaces CloudWatch support. -`metrics(namespace, [region])` | Returns a list of metrics in the namespace. (specify region for custom metrics) -`dimension_keys(namespace)` | Returns a list of dimension keys in the namespace. -`dimension_values(region, namespace, metric, dimension_key)` | Returns a list of dimension values matching the specified `region`, `namespace`, `metric` and `dimension_key`. -`ebs_volume_ids(region, instance_id)` | Returns a list of volume id matching the specified `region`, `instance_id`. -`ec2_instance_attribute(region, attribute_name, filters)` | Returns a list of attribute matching the specified `region`, `attribute_name`, `filters`. +*regions()* | Returns a list of regions AWS provides their service. +*namespaces()* | Returns a list of namespaces CloudWatch support. +*metrics(namespace, [region])* | Returns a list of metrics in the namespace. (specify region for custom metrics) +*dimension_keys(namespace)* | Returns a list of dimension keys in the namespace. +*dimension_values(region, namespace, metric, dimension_key)* | Returns a list of dimension values matching the specified `region`, `namespace`, `metric` and `dimension_key`. +*ebs_volume_ids(region, instance_id)* | Returns a list of volume id matching the specified `region`, `instance_id`. +*ec2_instance_attribute(region, attribute_name, filters)* | Returns a list of attribute matching the specified `region`, `attribute_name`, `filters`. For details about the metrics CloudWatch provides, please refer to the [CloudWatch documentation](https://docs.aws.amazon.com/AmazonCloudWatch/latest/DeveloperGuide/CW_Support_For_AWS.html). -## Example templated Queries +#### Examples templated Queries Example dimension queries which will return list of resources for individual AWS Services: -Service | Query +Query | Service ------- | ----- -ELB | `dimension_values(us-east-1,AWS/ELB,RequestCount,LoadBalancerName)` -ElastiCache | `dimension_values(us-east-1,AWS/ElastiCache,CPUUtilization,CacheClusterId)` -RedShift | `dimension_values(us-east-1,AWS/Redshift,CPUUtilization,ClusterIdentifier)` -RDS | `dimension_values(us-east-1,AWS/RDS,CPUUtilization,DBInstanceIdentifier)` -S3 | `dimension_values(us-east-1,AWS/S3,BucketSizeBytes,BucketName)` +*dimension_values(us-east-1,AWS/ELB,RequestCount,LoadBalancerName)* | ELB +*dimension_values(us-east-1,AWS/ElastiCache,CPUUtilization,CacheClusterId)* | ElastiCache +*dimension_values(us-east-1,AWS/Redshift,CPUUtilization,ClusterIdentifier)* | RedShift +*dimension_values(us-east-1,AWS/RDS,CPUUtilization,DBInstanceIdentifier)* | RDS +*dimension_values(us-east-1,AWS/S3,BucketSizeBytes,BucketName)* | S3 -## ec2_instance_attribute JSON filters +#### ec2_instance_attribute JSON filters The `ec2_instance_attribute` query take `filters` in JSON format. You can specify [pre-defined filters of ec2:DescribeInstances](http://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeInstances.html). -Specify like `{ filter_name1: [ filter_value1 ], filter_name2: [ filter_value2 ] }` + +Filters syntax: + +```javascript +{ filter_name1: [ filter_value1 ], filter_name2: [ filter_value2 ] } +``` Example `ec2_instance_attribute()` query ec2_instance_attribute(us-east-1, InstanceId, { "tag:Environment": [ "production" ] }) -![](/img/docs/v2/cloudwatch_templating.png) - ## Cost Amazon provides 1 million CloudWatch API requests each month at no additional charge. Past this, From 823b40a360b1403639727fa74e8406eef9b03adf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Mon, 8 May 2017 08:02:08 +0200 Subject: [PATCH 44/85] docs: added bash and http syntax highlighting --- docs/sources/http_api/dashboard.md | 142 +++++++++++++++------------- docs/sources/installation/debian.md | 42 +++++--- 2 files changed, 106 insertions(+), 78 deletions(-) diff --git a/docs/sources/http_api/dashboard.md b/docs/sources/http_api/dashboard.md index 053d4a8d329..a7b7ae87a62 100644 --- a/docs/sources/http_api/dashboard.md +++ b/docs/sources/http_api/dashboard.md @@ -19,26 +19,28 @@ Creates a new dashboard or updates an existing dashboard. **Example Request for new dashboard**: - POST /api/dashboards/db HTTP/1.1 - Accept: application/json - Content-Type: application/json - Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk +```http +POST /api/dashboards/db HTTP/1.1 +Accept: application/json +Content-Type: application/json +Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk - { - "dashboard": { - "id": null, - "title": "Production Overview", - "tags": [ "templated" ], - "timezone": "browser", - "rows": [ - { - } - ], - "schemaVersion": 6, - "version": 0 - }, - "overwrite": false - } +{ + "dashboard": { + "id": null, + "title": "Production Overview", + "tags": [ "templated" ], + "timezone": "browser", + "rows": [ + { + } + ], + "schemaVersion": 6, + "version": 0 + }, + "overwrite": false +} +``` JSON Body schema: @@ -47,15 +49,17 @@ JSON Body schema: **Example Response**: - HTTP/1.1 200 OK - Content-Type: application/json; charset=UTF-8 - Content-Length: 78 +```http +HTTP/1.1 200 OK +Content-Type: application/json; charset=UTF-8 +Content-Length: 78 - { - "slug": "production-overview", - "status": "success", - "version": 1 - } +{ + "slug": "production-overview", + "status": "success", + "version": 1 +} +``` Status Codes: @@ -67,14 +71,16 @@ Status Codes: The **412** status code is used when a newer dashboard already exists (newer, its version is greater than the version that was sent). The same status code is also used if another dashboard exists with the same title. The response body will look like this: - HTTP/1.1 412 Precondition Failed - Content-Type: application/json; charset=UTF-8 - Content-Length: 97 +```http +HTTP/1.1 412 Precondition Failed +Content-Type: application/json; charset=UTF-8 +Content-Length: 97 - { - "message": "The dashboard has been changed by someone else", - "status": "version-mismatch" - } +{ + "message": "The dashboard has been changed by someone else", + "status": "version-mismatch" +} +``` In in case of title already exists the `status` property will be `name-exists`. @@ -86,34 +92,38 @@ Will return the dashboard given the dashboard slug. Slug is the url friendly ver **Example Request**: - GET /api/dashboards/db/production-overview HTTP/1.1 - Accept: application/json - Content-Type: application/json - Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk +```http +GET /api/dashboards/db/production-overview HTTP/1.1 +Accept: application/json +Content-Type: application/json +Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk +``` **Example Response**: - HTTP/1.1 200 - Content-Type: application/json +```http +HTTP/1.1 200 +Content-Type: application/json - { - "meta": { - "isStarred": false, - "slug": "production-overview" - }, - "dashboard": { - "id": null, - "title": "Production Overview", - "tags": [ "templated" ], - "timezone": "browser", - "rows": [ - { - } - ], - "schemaVersion": 6, - "version": 0 +{ + "meta": { + "isStarred": false, + "slug": "production-overview" + }, + "dashboard": { + "id": null, + "title": "Production Overview", + "tags": [ "templated" ], + "timezone": "browser", + "rows": [ + { } - } + ], + "schemaVersion": 6, + "version": 0 + } +} +``` ## Delete dashboard @@ -123,17 +133,21 @@ The above will delete the dashboard with the specified slug. The slug is the url **Example Request**: - DELETE /api/dashboards/db/test HTTP/1.1 - Accept: application/json - Content-Type: application/json - Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk +```http +DELETE /api/dashboards/db/test HTTP/1.1 +Accept: application/json +Content-Type: application/json +Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk +``` **Example Response**: - HTTP/1.1 200 - Content-Type: application/json +```http +HTTP/1.1 200 +Content-Type: application/json - {"title": "Test"} +{"title": "Test"} +``` ## Gets the home dashboard diff --git a/docs/sources/installation/debian.md b/docs/sources/installation/debian.md index 2071ff1b79c..7bbf8c2052f 100644 --- a/docs/sources/installation/debian.md +++ b/docs/sources/installation/debian.md @@ -22,10 +22,10 @@ installation. ## Install Stable -``` -$ wget https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana_4.2.0_amd64.deb -$ sudo apt-get install -y adduser libfontconfig -$ sudo dpkg -i grafana_4.2.0_amd64.deb +```bash +wget https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana_4.2.0_amd64.deb +sudo apt-get install -y adduser libfontconfig +sudo dpkg -i grafana_4.2.0_amd64.deb ``` ## APT Repository @@ -43,18 +43,24 @@ candidates. Then add the [Package Cloud](https://packagecloud.io/grafana) key. This allows you to install signed packages. - $ curl https://packagecloud.io/gpg.key | sudo apt-key add - +```bash +curl https://packagecloud.io/gpg.key | sudo apt-key add - +``` Update your Apt repositories and install Grafana - $ sudo apt-get update - $ sudo apt-get install grafana +```bash +sudo apt-get update +sudo apt-get install grafana +``` On some older versions of Ubuntu and Debian you may need to install the `apt-transport-https` package which is needed to fetch packages over HTTPS. - $ sudo apt-get install -y apt-transport-https +```bash +sudo apt-get install -y apt-transport-https +``` ## Package details @@ -70,7 +76,9 @@ HTTPS. Start Grafana by running: - $ sudo service grafana-server start +```bash +sudo service grafana-server start +``` This will start the `grafana-server` process as the `grafana` user, which was created during the package installation. The default HTTP port @@ -78,19 +86,25 @@ is `3000` and default user and group is `admin`. To configure the Grafana server to start at boot time: - $ sudo update-rc.d grafana-server defaults +```bash +sudo update-rc.d grafana-server defaults +``` ## Start the server (via systemd) To start the service using systemd: - $ systemctl daemon-reload - $ systemctl start grafana-server - $ systemctl status grafana-server +```bash +systemctl daemon-reload +systemctl start grafana-server +systemctl status grafana-server +``` Enable the systemd service so that Grafana starts at boot. - sudo systemctl enable grafana-server.service +```bash +sudo systemctl enable grafana-server.service +``` ## Environment file From 556829eda9bd9c06fa77b7acacdbbcfb14aeecb0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Mon, 8 May 2017 11:17:29 +0200 Subject: [PATCH 45/85] table: began table options redesign --- public/app/plugins/panel/table/editor.html | 141 +++++++++++---------- public/app/plugins/panel/table/editor.ts | 5 +- public/sass/components/_tabs.scss | 9 ++ 3 files changed, 85 insertions(+), 70 deletions(-) diff --git a/public/app/plugins/panel/table/editor.html b/public/app/plugins/panel/table/editor.html index 561645b7118..657771d8338 100644 --- a/public/app/plugins/panel/table/editor.html +++ b/public/app/plugins/panel/table/editor.html @@ -52,58 +52,62 @@
    -
    -
    -
    Column Styles
    -
    +
    + Style Rules + +
    + + + +
    + +
    +
    +
    + + +
    +
    + +
    + +
    +
    +
    +
    + + +
    +
    + +
    +
    Date options
    +
    + + +
    +
    + +
    +
    String options
    + +
    + +
    + +
    +
    Number options
    +
    - - -
    -
    - -
    - -
    -
    -
    - - -
    -
    -
    -
    -
    - -
    -
    - -
    -
    - -
    -
    - -
    -
    -
    -
    -
    - -
    - -
    -
    -
    -
    - -
    -
    @@ -113,22 +117,23 @@
    +
    +
    + +
    +
    Thresholds & Coloring
    +
    +
    + + +
    -
    -
    -
    -
    -
    -
    - - -
    @@ -140,19 +145,17 @@ -
    -
    -
    +
    +
    +
    -
    -
    -
    - -
    + +
    +
    diff --git a/public/app/plugins/panel/table/editor.ts b/public/app/plugins/panel/table/editor.ts index 9f36c88c986..fca7b8a90fb 100644 --- a/public/app/plugins/panel/table/editor.ts +++ b/public/app/plugins/panel/table/editor.ts @@ -21,10 +21,12 @@ export class TablePanelEditorCtrl { addColumnSegment: any; unitFormats: any; getColumnNames: any; + activeStyleIndex: number; /** @ngInject */ constructor($scope, private $q, private uiSegmentSrv) { $scope.editor = this; + this.activeStyleIndex = 0; this.panelCtrl = $scope.ctrl; this.panel = this.panelCtrl.panel; this.transformers = transformers; @@ -111,12 +113,13 @@ export class TablePanelEditorCtrl { decimals: 2, colors: ["rgba(245, 54, 54, 0.9)", "rgba(237, 129, 40, 0.89)", "rgba(50, 172, 45, 0.97)"], colorMode: null, - pattern: '/.*/', + pattern: '', dateFormat: 'YYYY-MM-DD HH:mm:ss', thresholds: [], }; this.panel.styles.push(angular.copy(columnStyleDefaults)); + this.activeStyleIndex = this.panel.styles.length-1; } removeColumnStyle(style) { diff --git a/public/sass/components/_tabs.scss b/public/sass/components/_tabs.scss index 2426f70afee..32e395c4d73 100644 --- a/public/sass/components/_tabs.scss +++ b/public/sass/components/_tabs.scss @@ -68,3 +68,12 @@ top: 1px; } } + +.form-tabs-wrapper { + @include brand-bottom-border(); + @include clearfix(); +} + +.form-tabs-content { + padding: $spacer*2 $spacer; +} From be284adaccaafb8dcc07dd5ce7278c354c7c6f9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Mon, 8 May 2017 15:26:05 +0200 Subject: [PATCH 46/85] table: more table options refactoring --- public/app/plugins/panel/table/editor.html | 117 ++++++++++----------- public/app/plugins/panel/table/editor.ts | 18 +++- 2 files changed, 69 insertions(+), 66 deletions(-) diff --git a/public/app/plugins/panel/table/editor.html b/public/app/plugins/panel/table/editor.html index 657771d8338..5697bebe97d 100644 --- a/public/app/plugins/panel/table/editor.html +++ b/public/app/plugins/panel/table/editor.html @@ -53,7 +53,7 @@
    - Style Rules + Column Style Rules @@ -69,90 +69,81 @@
    -
    +
    +
    Options
    -
    - -
    - -
    -
    - +
    -
    -
    Date options
    +
    +
    Type
    +
    + +
    + +
    +
    +
    -
    -
    -
    String options
    - -
    - -
    - -
    -
    Number options
    - -
    -
    - -
    -
    -
    -
    -
    - - -
    -
    +
    +
    -
    -
    Thresholds & Coloring
    -
    -
    - - -
    -
    - -
    - -
    -
    - -
    - - - - - - - - - - -
    - Invert -
    -
    +
    +
    + +
    +
    +
    + +
    +
    +
    Thresholds
    +
    + + +
    +
    + +
    + +
    +
    +
    + + + + + + + + + + +
    + Invert +
    +
    +
    + +
    + diff --git a/public/app/plugins/panel/table/editor.ts b/public/app/plugins/panel/table/editor.ts index fca7b8a90fb..9379702e7db 100644 --- a/public/app/plugins/panel/table/editor.ts +++ b/public/app/plugins/panel/table/editor.ts @@ -106,7 +106,7 @@ export class TablePanelEditorCtrl { } addColumnStyle() { - var columnStyleDefaults = { + var newStyleRule = { unit: 'short', type: 'number', alias: '', @@ -118,8 +118,20 @@ export class TablePanelEditorCtrl { thresholds: [], }; - this.panel.styles.push(angular.copy(columnStyleDefaults)); - this.activeStyleIndex = this.panel.styles.length-1; + var styles = this.panel.styles; + var stylesCount = styles.length; + var indexToInsert = stylesCount; + + // check if last is a catch all rule, then add it before that one + if (stylesCount > 0) { + var last = styles[stylesCount-1]; + if (last.pattern === '/.*/') { + indexToInsert = stylesCount-1; + } + } + + styles.splice(indexToInsert, 0, newStyleRule); + this.activeStyleIndex = indexToInsert; } removeColumnStyle(style) { From f168c9e53dd3a37dfbd757878f42ad655960250f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Mon, 8 May 2017 15:57:15 +0200 Subject: [PATCH 47/85] table: minor ux options update --- public/app/plugins/panel/table/editor.html | 172 ++++++++++----------- 1 file changed, 85 insertions(+), 87 deletions(-) diff --git a/public/app/plugins/panel/table/editor.html b/public/app/plugins/panel/table/editor.html index 5697bebe97d..7eae11d7b0a 100644 --- a/public/app/plugins/panel/table/editor.html +++ b/public/app/plugins/panel/table/editor.html @@ -29,108 +29,106 @@
    Table Display
    -
    -
    - - -
    - -
    - -
    - -
    -
    -
    -
    +
    + + +
    + +
    + +
    + +
    +
    +
    - Column Style Rules - + Column Style Rules +
    -
    -
    Options
    -
    -
    - - -
    -
    -
    - - -
    -
    +
    +
    Options
    +
    +
    + + +
    +
    +
    + + +
    +
    -
    -
    Type
    +
    +
    Type
    -
    - -
    - -
    -
    -
    - - -
    +
    + +
    + +
    +
    +
    + + +
    -
    - -
    +
    + +
    -
    -
    - -
    -
    -
    - - -
    -
    -
    +
    +
    + +
    +
    +
    + + +
    +
    +
    -
    -
    Thresholds
    -
    - - -
    -
    - -
    - -
    -
    -
    - - - - - +
    +
    Thresholds
    +
    + + +
    +
    + +
    + +
    +
    +
    + + + + + From 25a267a7ab3d7db71724cbf5f5b9b369425c1c03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Mon, 8 May 2017 16:08:53 +0200 Subject: [PATCH 48/85] fix: segment used influxdb naming for variable in options callback, should be more generic --- public/app/core/directives/metric_segment.js | 2 +- .../app/plugins/datasource/influxdb/partials/query.editor.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/public/app/core/directives/metric_segment.js b/public/app/core/directives/metric_segment.js index c3e51dc7a0c..2e9442c15a0 100644 --- a/public/app/core/directives/metric_segment.js +++ b/public/app/core/directives/metric_segment.js @@ -77,7 +77,7 @@ function (_, $, coreModule) { $scope.source = function(query, callback) { $scope.$apply(function() { - $scope.getOptions({ measurementFilter: query }).then(function(altSegments) { + $scope.getOptions({ $query: query }).then(function(altSegments) { $scope.altSegments = altSegments; options = _.map($scope.altSegments, function(alt) { return alt.value; }); diff --git a/public/app/plugins/datasource/influxdb/partials/query.editor.html b/public/app/plugins/datasource/influxdb/partials/query.editor.html index bff03fbb7d5..31bc7e6d8ad 100644 --- a/public/app/plugins/datasource/influxdb/partials/query.editor.html +++ b/public/app/plugins/datasource/influxdb/partials/query.editor.html @@ -11,7 +11,7 @@ - +
    From fdc97010b4293a6bb09519378ac4f76d53d1b56a Mon Sep 17 00:00:00 2001 From: Anton Yackushev Date: Tue, 9 May 2017 08:16:48 +0300 Subject: [PATCH 49/85] Rename fielddata_fields to docvalue_fields (#8317) The parameter fielddata_fields is deprecated and removed in 5.0 https://github.com/elastic/elasticsearch/issues/19027 --- public/app/plugins/datasource/elasticsearch/query_builder.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/app/plugins/datasource/elasticsearch/query_builder.js b/public/app/plugins/datasource/elasticsearch/query_builder.js index fbbd4f7e929..dd296c00a34 100644 --- a/public/app/plugins/datasource/elasticsearch/query_builder.js +++ b/public/app/plugins/datasource/elasticsearch/query_builder.js @@ -119,7 +119,7 @@ function (queryDef) { } query.script_fields = {}, - query.fielddata_fields = [this.timeField]; + query.docvalue_fields = [this.timeField]; return query; }; From 8d072de556a0fa7db279b32b778074d2f9c3ce3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Tue, 9 May 2017 08:33:37 +0200 Subject: [PATCH 50/85] elasticsearch: changed default terms min_doc_count to 1 and order by to desc, closes #8231 --- public/app/plugins/datasource/elasticsearch/bucket_agg.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/public/app/plugins/datasource/elasticsearch/bucket_agg.js b/public/app/plugins/datasource/elasticsearch/bucket_agg.js index d6d641c1655..b2cfc819579 100644 --- a/public/app/plugins/datasource/elasticsearch/bucket_agg.js +++ b/public/app/plugins/datasource/elasticsearch/bucket_agg.js @@ -81,9 +81,9 @@ function (angular, _, queryDef) { switch($scope.agg.type) { case 'terms': { - settings.order = settings.order || "asc"; + settings.order = settings.order || "desc"; settings.size = settings.size || "10"; - settings.min_doc_count = settings.min_doc_count || 0; + settings.min_doc_count = settings.min_doc_count || 1; settings.orderBy = settings.orderBy || "_term"; if (settings.size !== '0') { From 84141eb14ae0e0d7394c7b41736243b1e4059836 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Tue, 9 May 2017 08:45:47 +0200 Subject: [PATCH 51/85] docs: updated changelog --- CHANGELOG.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 30bcc05597a..022684f9fbc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,6 @@ * **Prometheus**: Support table response formats (column per label) [#6140](https://github.com/grafana/grafana/issues/6140), thx [@mtanda](https://github.com/mtanda) * **Single Stat Panel**: support for non time series data [#6564](https://github.com/grafana/grafana/issues/6564) - ## Minor Enchancements * **Prometheus**: Make Prometheus query field a textarea [#7663](https://github.com/grafana/grafana/issues/7663), thx [@hagen1778](https://github.com/hagen1778) @@ -22,12 +21,16 @@ * **Templating**: Data source variable now supports multi value and panel repeats [#7030](https://github.com/grafana/grafana/issues/7030) thx [@mtanda](https://github.com/mtanda) * **Telegram**: Telegram alert is not sending metric and legend. [#8110](https://github.com/grafana/grafana/issues/8110), thx [@bashgeek](https://github.com/bashgeek) * **Graph**: Support dashed lines [#514](https://github.com/grafana/grafana/issues/514), thx [@smalik03](https://github.com/smalik03) +* **Table**: Support to change column header text [#3551](https://github.com/grafana/grafana/issues/3551) ## Fixes * **Table Panel**: Fixed annotation display in table panel, [#8023](https://github.com/grafana/grafana/issues/8023) * **Dashboard**: If refresh is blocked due to tab not visible, then refresh when it becomes visible [#8076](https://github.com/grafana/grafana/issues/8076) thanks [@SimenB](https://github.com/SimenB) * **Snapshots**: Fixed problem with annotations & snapshots [#7659](https://github.com/grafana/grafana/issues/7659) +## Changes +* **Elasticsearch**: Changed elasticsearch Terms aggregation to default to Min Doc Count to 1, and sort order to Top [#8321](https://github.com/grafana/grafana/issues/8321) + # 4.2.0 (2017-03-22) ## Minor Enhancements * **Templates**: Prevent use of the prefix `__` for templates in web UI [#7678](https://github.com/grafana/grafana/issues/7678) From e218052a904b5eaa27d9b486adc03e502984e845 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Tue, 9 May 2017 12:07:06 +0200 Subject: [PATCH 52/85] fix: fixed slow down issue in table panel by removing the fillter null values feature (#7602), not sure the filter null values is a good table option, should be done in metric query, fixes #8234 --- public/app/plugins/panel/table/editor.html | 4 ---- public/app/plugins/panel/table/module.ts | 1 - public/app/plugins/panel/table/transformers.ts | 12 ++---------- 3 files changed, 2 insertions(+), 15 deletions(-) diff --git a/public/app/plugins/panel/table/editor.html b/public/app/plugins/panel/table/editor.html index 7eae11d7b0a..77860266b45 100644 --- a/public/app/plugins/panel/table/editor.html +++ b/public/app/plugins/panel/table/editor.html @@ -21,10 +21,6 @@
    -
    diff --git a/public/app/plugins/panel/table/module.ts b/public/app/plugins/panel/table/module.ts index 9b563ca5b18..06475caa332 100644 --- a/public/app/plugins/panel/table/module.ts +++ b/public/app/plugins/panel/table/module.ts @@ -45,7 +45,6 @@ class TablePanelCtrl extends MetricsPanelCtrl { scroll: true, fontSize: '100%', sort: {col: 0, desc: true}, - filterNull: false, }; /** @ngInject */ diff --git a/public/app/plugins/panel/table/transformers.ts b/public/app/plugins/panel/table/transformers.ts index 0f793fa0434..c7f957e0156 100644 --- a/public/app/plugins/panel/table/transformers.ts +++ b/public/app/plugins/panel/table/transformers.ts @@ -220,8 +220,7 @@ transformers['json'] = { }; function transformDataToTable(data, panel) { - var model = new TableModel(), - copyData = angular.copy(data); + var model = new TableModel(); if (!data || data.length === 0) { return model; @@ -232,14 +231,7 @@ function transformDataToTable(data, panel) { throw {message: 'Transformer ' + panel.transform + ' not found'}; } - if (panel.filterNull) { - for (var i = 0; i < copyData.length; i++) { - copyData[i].datapoints = copyData[i].datapoints.filter((dp) => dp[0] != null); - } - } - - transformer.transform(copyData, panel, model); - + transformer.transform(data, panel, model); return model; } From 8bbff2c44eb998e513e5264b2d368c558590c1a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Tue, 9 May 2017 12:35:44 +0200 Subject: [PATCH 53/85] table: refactoring table options, now column styles are in a seperate tab --- .../plugins/panel/table/column_options.html | 99 +++++++++++++++ .../app/plugins/panel/table/column_options.ts | 120 ++++++++++++++++++ public/app/plugins/panel/table/editor.html | 101 +-------------- public/app/plugins/panel/table/editor.ts | 81 ------------ public/app/plugins/panel/table/module.ts | 2 + public/sass/components/edit_sidemenu.scss | 3 +- 6 files changed, 224 insertions(+), 182 deletions(-) create mode 100644 public/app/plugins/panel/table/column_options.html create mode 100644 public/app/plugins/panel/table/column_options.ts diff --git a/public/app/plugins/panel/table/column_options.html b/public/app/plugins/panel/table/column_options.html new file mode 100644 index 00000000000..35fa4ac2d8b --- /dev/null +++ b/public/app/plugins/panel/table/column_options.html @@ -0,0 +1,99 @@ + +
    + + +
    + +
    +
    Options
    +
    +
    + + +
    +
    +
    + + +
    +
    + +
    +
    Type
    + +
    + +
    + +
    +
    +
    + + +
    + +
    + +
    + +
    +
    + +
    +
    +
    + + +
    +
    +
    + +
    +
    Thresholds
    +
    + + +
    +
    + +
    + +
    +
    +
    + + + + + + + + + + +
    + Invert +
    +
    +
    + +
    + + +
    +
    + +
    diff --git a/public/app/plugins/panel/table/column_options.ts b/public/app/plugins/panel/table/column_options.ts new file mode 100644 index 00000000000..5109f87f86d --- /dev/null +++ b/public/app/plugins/panel/table/column_options.ts @@ -0,0 +1,120 @@ +/// + + +import _ from 'lodash'; +import $ from 'jquery'; +import moment from 'moment'; +import angular from 'angular'; + +import kbn from 'app/core/utils/kbn'; + +export class ColumnOptionsCtrl { + panel: any; + panelCtrl: any; + colorModes: any; + columnStyles: any; + columnTypes: any; + fontSizes: any; + dateFormats: any; + addColumnSegment: any; + unitFormats: any; + getColumnNames: any; + activeStyleIndex: number; + + /** @ngInject */ + constructor($scope, private $q, private uiSegmentSrv) { + $scope.editor = this; + this.activeStyleIndex = 0; + this.panelCtrl = $scope.ctrl; + this.panel = this.panelCtrl.panel; + this.unitFormats = kbn.getUnitFormats(); + this.colorModes = [ + {text: 'Disabled', value: null}, + {text: 'Cell', value: 'cell'}, + {text: 'Value', value: 'value'}, + {text: 'Row', value: 'row'}, + ]; + this.columnTypes = [ + {text: 'Number', value: 'number'}, + {text: 'String', value: 'string'}, + {text: 'Date', value: 'date'}, + {text: 'Hidden', value: 'hidden'} + ]; + this.fontSizes = ['80%', '90%', '100%', '110%', '120%', '130%', '150%', '160%', '180%', '200%', '220%', '250%']; + this.dateFormats = [ + {text: 'YYYY-MM-DD HH:mm:ss', value: 'YYYY-MM-DD HH:mm:ss'}, + {text: 'MM/DD/YY h:mm:ss a', value: 'MM/DD/YY h:mm:ss a'}, + {text: 'MMMM D, YYYY LT', value: 'MMMM D, YYYY LT'}, + ]; + + this.getColumnNames = () => { + if (!this.panelCtrl.table) { + return []; + } + return _.map(this.panelCtrl.table.columns, function(col: any) { + return col.text; + }); + }; + } + + render() { + this.panelCtrl.render(); + } + + setUnitFormat(column, subItem) { + column.unit = subItem.value; + this.panelCtrl.render(); + } + + addColumnStyle() { + var newStyleRule = { + unit: 'short', + type: 'number', + alias: '', + decimals: 2, + colors: ["rgba(245, 54, 54, 0.9)", "rgba(237, 129, 40, 0.89)", "rgba(50, 172, 45, 0.97)"], + colorMode: null, + pattern: '', + dateFormat: 'YYYY-MM-DD HH:mm:ss', + thresholds: [], + }; + + var styles = this.panel.styles; + var stylesCount = styles.length; + var indexToInsert = stylesCount; + + // check if last is a catch all rule, then add it before that one + if (stylesCount > 0) { + var last = styles[stylesCount-1]; + if (last.pattern === '/.*/') { + indexToInsert = stylesCount-1; + } + } + + styles.splice(indexToInsert, 0, newStyleRule); + this.activeStyleIndex = indexToInsert; + } + + removeColumnStyle(style) { + this.panel.styles = _.without(this.panel.styles, style); + } + + invertColorOrder(index) { + var ref = this.panel.styles[index].colors; + var copy = ref[0]; + ref[0] = ref[2]; + ref[2] = copy; + this.panelCtrl.render(); + } +} + +/** @ngInject */ +export function columnOptionsTab($q, uiSegmentSrv) { + 'use strict'; + return { + restrict: 'E', + scope: true, + templateUrl: 'public/app/plugins/panel/table/column_options.html', + controller: ColumnOptionsCtrl, + }; +} diff --git a/public/app/plugins/panel/table/editor.html b/public/app/plugins/panel/table/editor.html index 77860266b45..9854ac26dc3 100644 --- a/public/app/plugins/panel/table/editor.html +++ b/public/app/plugins/panel/table/editor.html @@ -24,7 +24,7 @@
    -
    Table Display
    +
    Paging
    - -
    - Column Style Rules - -
    - - - -
    - -
    -
    Options
    -
    -
    - - -
    -
    -
    - - -
    -
    - -
    -
    Type
    - -
    - -
    - -
    -
    -
    - - -
    - -
    - -
    - -
    -
    - -
    -
    -
    - - -
    -
    -
    - -
    -
    Thresholds
    -
    - - -
    -
    - -
    - -
    -
    -
    - - - - - - - - - - -
    - Invert -
    -
    -
    - -
    - - -
    -
    -
    diff --git a/public/app/plugins/panel/table/editor.ts b/public/app/plugins/panel/table/editor.ts index 9379702e7db..ce5d0c1c828 100644 --- a/public/app/plugins/panel/table/editor.ts +++ b/public/app/plugins/panel/table/editor.ts @@ -13,54 +13,19 @@ export class TablePanelEditorCtrl { panel: any; panelCtrl: any; transformers: any; - colorModes: any; - columnStyles: any; - columnTypes: any; fontSizes: any; - dateFormats: any; addColumnSegment: any; - unitFormats: any; getColumnNames: any; - activeStyleIndex: number; /** @ngInject */ constructor($scope, private $q, private uiSegmentSrv) { $scope.editor = this; - this.activeStyleIndex = 0; this.panelCtrl = $scope.ctrl; this.panel = this.panelCtrl.panel; this.transformers = transformers; - this.unitFormats = kbn.getUnitFormats(); - this.colorModes = [ - {text: 'Disabled', value: null}, - {text: 'Cell', value: 'cell'}, - {text: 'Value', value: 'value'}, - {text: 'Row', value: 'row'}, - ]; - this.columnTypes = [ - {text: 'Number', value: 'number'}, - {text: 'String', value: 'string'}, - {text: 'Date', value: 'date'}, - {text: 'Hidden', value: 'hidden'} - ]; this.fontSizes = ['80%', '90%', '100%', '110%', '120%', '130%', '150%', '160%', '180%', '200%', '220%', '250%']; - this.dateFormats = [ - {text: 'YYYY-MM-DD HH:mm:ss', value: 'YYYY-MM-DD HH:mm:ss'}, - {text: 'MM/DD/YY h:mm:ss a', value: 'MM/DD/YY h:mm:ss a'}, - {text: 'MMMM D, YYYY LT', value: 'MMMM D, YYYY LT'}, - ]; this.addColumnSegment = uiSegmentSrv.newPlusButton(); - - // this is used from bs-typeahead and needs to be instance bound - this.getColumnNames = () => { - if (!this.panelCtrl.table) { - return []; - } - return _.map(this.panelCtrl.table.columns, function(col: any) { - return col.text; - }); - }; } getColumnOptions() { @@ -99,52 +64,6 @@ export class TablePanelEditorCtrl { this.panel.columns = _.without(this.panel.columns, column); this.panelCtrl.render(); } - - setUnitFormat(column, subItem) { - column.unit = subItem.value; - this.panelCtrl.render(); - } - - addColumnStyle() { - var newStyleRule = { - unit: 'short', - type: 'number', - alias: '', - decimals: 2, - colors: ["rgba(245, 54, 54, 0.9)", "rgba(237, 129, 40, 0.89)", "rgba(50, 172, 45, 0.97)"], - colorMode: null, - pattern: '', - dateFormat: 'YYYY-MM-DD HH:mm:ss', - thresholds: [], - }; - - var styles = this.panel.styles; - var stylesCount = styles.length; - var indexToInsert = stylesCount; - - // check if last is a catch all rule, then add it before that one - if (stylesCount > 0) { - var last = styles[stylesCount-1]; - if (last.pattern === '/.*/') { - indexToInsert = stylesCount-1; - } - } - - styles.splice(indexToInsert, 0, newStyleRule); - this.activeStyleIndex = indexToInsert; - } - - removeColumnStyle(style) { - this.panel.styles = _.without(this.panel.styles, style); - } - - invertColorOrder(index) { - var ref = this.panel.styles[index].colors; - var copy = ref[0]; - ref[0] = ref[2]; - ref[2] = copy; - this.panelCtrl.render(); - } } /** @ngInject */ diff --git a/public/app/plugins/panel/table/module.ts b/public/app/plugins/panel/table/module.ts index 06475caa332..8acf14f56ca 100644 --- a/public/app/plugins/panel/table/module.ts +++ b/public/app/plugins/panel/table/module.ts @@ -8,6 +8,7 @@ import * as FileExport from 'app/core/utils/file_export'; import {MetricsPanelCtrl} from 'app/plugins/sdk'; import {transformDataToTable} from './transformers'; import {tablePanelEditor} from './editor'; +import {columnOptionsTab} from './column_options'; import {TableRenderer} from './renderer'; class TablePanelCtrl extends MetricsPanelCtrl { @@ -70,6 +71,7 @@ class TablePanelCtrl extends MetricsPanelCtrl { onInitEditMode() { this.addEditorTab('Options', tablePanelEditor, 2); + this.addEditorTab('Column Styles', columnOptionsTab, 3); } onInitPanelActions(actions) { diff --git a/public/sass/components/edit_sidemenu.scss b/public/sass/components/edit_sidemenu.scss index 83f845c08e1..d7844ab6f36 100644 --- a/public/sass/components/edit_sidemenu.scss +++ b/public/sass/components/edit_sidemenu.scss @@ -10,7 +10,8 @@ } .edit-sidemenu-aside { - min-width: 15rem; + min-width: 6rem; + margin-right: $spacer*2; } .edit-sidemenu { From d791f902e9cce7a0fa53b0cdd59a42d7fb9e3641 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Wed, 10 May 2017 13:05:26 +0200 Subject: [PATCH 54/85] heatmap: more refactoring --- .../panel/heatmap/heatmap_data_converter.ts | 28 ++++++++----------- .../plugins/panel/heatmap/heatmap_tooltip.ts | 25 +++++------------ 2 files changed, 19 insertions(+), 34 deletions(-) diff --git a/public/app/plugins/panel/heatmap/heatmap_data_converter.ts b/public/app/plugins/panel/heatmap/heatmap_data_converter.ts index f627173d233..0136a1bb511 100644 --- a/public/app/plugins/panel/heatmap/heatmap_data_converter.ts +++ b/public/app/plugins/panel/heatmap/heatmap_data_converter.ts @@ -92,7 +92,8 @@ function mergeZeroBuckets(buckets, minValue) { let emptyBucket = { bounds: {bottom: 0, top: 0}, values: [], - points: [] + points: [], + count: 0, }; let nullBucket = yBuckets[0] || emptyBucket; @@ -102,25 +103,20 @@ function mergeZeroBuckets(buckets, minValue) { y: 0, bounds: {bottom: minValue, top: minBucket.bounds.top || minValue}, values: [], - points: [] + points: [], + count: 0, }; - if (nullBucket.values) { - newBucket.values = nullBucket.values.concat(minBucket.values); - } - if (nullBucket.points) { - newBucket.points = nullBucket.points.concat(minBucket.points); + newBucket.points = nullBucket.points.concat(minBucket.points); + newBucket.values = nullBucket.values.concat(minBucket.values); + newBucket.count = newBucket.values.length; + + if (newBucket.count === 0) { + return; } - let newYBuckets = {}; - _.forEach(yBuckets, (bucket, bound) => { - bound = Number(bound); - if (bound !== 0 && bound !== minValue) { - newYBuckets[bound] = bucket; - } - }); - newYBuckets[0] = newBucket; - xBucket.buckets = newYBuckets; + delete yBuckets[minValue]; + yBuckets[0] = newBucket; }); return buckets; diff --git a/public/app/plugins/panel/heatmap/heatmap_tooltip.ts b/public/app/plugins/panel/heatmap/heatmap_tooltip.ts index 28824d7c7e2..d2152c68aa7 100644 --- a/public/app/plugins/panel/heatmap/heatmap_tooltip.ts +++ b/public/app/plugins/panel/heatmap/heatmap_tooltip.ts @@ -33,7 +33,7 @@ export class HeatmapTooltip { } onMouseOver(e) { - if (!this.panel.tooltip.show || _.isEmpty(this.scope.ctrl.data.buckets)) { return; } + if (!this.panel.tooltip.show || !this.scope.ctrl.data || _.isEmpty(this.scope.ctrl.data.buckets)) { return; } if (!this.tooltip) { this.add(); @@ -67,6 +67,10 @@ export class HeatmapTooltip { show(pos, data) { if (!this.panel.tooltip.show || !data) { return; } + // shared tooltip mode + if (pos.panelRelY) { + return; + } let {xBucketIndex, yBucketIndex} = this.getBucketIndexes(pos, data); @@ -120,23 +124,8 @@ export class HeatmapTooltip { } getBucketIndexes(pos, data) { - let xBucketIndex, yBucketIndex; - - // if panelRelY is defined another panel wants us to show a tooltip - if (pos.panelRelY) { - xBucketIndex = getValueBucketBound(pos.x, data.xBucketSize, 1); - let y = this.scope.yScale.invert(pos.panelRelY * this.scope.chartHeight); - yBucketIndex = getValueBucketBound(y, data.yBucketSize, this.panel.yAxis.logBase); - pos = this.getSharedTooltipPos(pos); - - if (!this.tooltip) { - // Add shared tooltip for panel - this.add(); - } - } else { - xBucketIndex = this.getXBucketIndex(pos.offsetX, data); - yBucketIndex = this.getYBucketIndex(pos.offsetY, data); - } + const xBucketIndex = this.getXBucketIndex(pos.offsetX, data); + const yBucketIndex = this.getYBucketIndex(pos.offsetY, data); return {xBucketIndex, yBucketIndex}; } From c21f86f6b4c58ee6d80101e383719c0ba49404fe Mon Sep 17 00:00:00 2001 From: Daniel Lee Date: Wed, 10 May 2017 13:29:36 +0200 Subject: [PATCH 55/85] Update CHANGELOG.md ref #8277, ref #8250, ref #8262, ref #8165, ref #8093, ref #8056, ref #8043, ref #7970, ref #7914, ref #7864, ref #7750, ref #7740, ref #7697, ref #7619, ref #5619, ref #4030, ref #5278, ref #3302, ref #2524 --- CHANGELOG.md | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 022684f9fbc..bf13b4a51e7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ * **Graph**: Support for histograms [#600](https://github.com/grafana/grafana/issues/600) * **Prometheus**: Support table response formats (column per label) [#6140](https://github.com/grafana/grafana/issues/6140), thx [@mtanda](https://github.com/mtanda) * **Single Stat Panel**: support for non time series data [#6564](https://github.com/grafana/grafana/issues/6564) +* **Server**: Monitoring Grafana (health check endpoint) [#3302](https://github.com/grafana/grafana/issues/3302) ## Minor Enchancements @@ -22,15 +23,43 @@ * **Telegram**: Telegram alert is not sending metric and legend. [#8110](https://github.com/grafana/grafana/issues/8110), thx [@bashgeek](https://github.com/bashgeek) * **Graph**: Support dashed lines [#514](https://github.com/grafana/grafana/issues/514), thx [@smalik03](https://github.com/smalik03) * **Table**: Support to change column header text [#3551](https://github.com/grafana/grafana/issues/3551) +* **Alerting**: Better error when SMTP is not configured [#8093](https://github.com/grafana/grafana/issues/8093) +* **Pushover**: Add an option to attach graph image link in Pushover notification [#8043](https://github.com/grafana/grafana/issues/8043) thx [@devkid](https://github.com/devkid) +* **WebDAV**: Allow to set different ImageBaseUrl for WebDAV upload and image link [#7914](https://github.com/grafana/grafana/issues/7914) +* **Panels**: type-ahead mixed datasource selection [#7697](https://github.com/grafana/grafana/issues/7697) thx [@mtanda](https://github.com/mtanda) +* **Security**:User enumeration problem [#7619](https://github.com/grafana/grafana/issues/7619) +* **InfluxDB**: Register new queries available in InfluxDB - Holt Winters [#5619](https://github.com/grafana/grafana/issues/5619) thx [@rikkuness](https://github.com/rikkuness) +* **Server**: Support listening on a UNIX socket [#4030](https://github.com/grafana/grafana/issues/4030), thx [@mitjaziv](https://github.com/mitjaziv) +* **Graph**: Support log scaling for values smaller 1 [#5278](https://github.com/grafana/grafana/issues/5278) +* **InfluxDB**: Slow 'select measurement' rendering for InfluxDB [#2524](https://github.com/grafana/grafana/issues/2524), thx [@sbhenderson](https://github.com/sbhenderson) ## Fixes * **Table Panel**: Fixed annotation display in table panel, [#8023](https://github.com/grafana/grafana/issues/8023) * **Dashboard**: If refresh is blocked due to tab not visible, then refresh when it becomes visible [#8076](https://github.com/grafana/grafana/issues/8076) thanks [@SimenB](https://github.com/SimenB) * **Snapshots**: Fixed problem with annotations & snapshots [#7659](https://github.com/grafana/grafana/issues/7659) +* **Graph**: MetricSegment loses type when value is an asterisk [#8277](https://github.com/grafana/grafana/issues/8277), thx [@Gordiychuk](https://github.com/Gordiychuk) +* **Alerting**: Alert notifications do not show charts when using a non public S3 bucket [#8250](https://github.com/grafana/grafana/issues/8250) thx [@rogerswingle](https://github.com/rogerswingle) +* **Graph**: 100% client CPU usage on red alert glow animation [#8222](https://github.com/grafana/grafana/issues/8222) +* **InfluxDB**: Templating: "All" query does match too much [#8165](https://github.com/grafana/grafana/issues/8165) +* **Dashboard**: Description tooltip is not fully displayed [#7970](https://github.com/grafana/grafana/issues/7970) +* **Proxy**: Redirect after switching Org does not obey sub path in root_url (using reverse proxy) [#8089](https://github.com/grafana/grafana/issues/8089) +* **Templating**: Restoration of ad-hoc variable from URL does not work correctly [#8056](https://github.com/grafana/grafana/issues/8056) thx [@tamayika](https://github.com/tamayika) +* **InfluxDB**: timeFilter cannot be used twice in alerts [#7969](https://github.com/grafana/grafana/issues/7969) +* **MySQL**: 4-byte UTF8 not supported when using MySQL database (allows Emojis) [#7958](https://github.com/grafana/grafana/issues/7958) +* **Alerting**: api/alerts and api/alert/:id hold previous data for "message" and "Message" field when field value is changed from "some string" to empty string. [#7927](https://github.com/grafana/grafana/issues/7927) +* **Graph**: Cannot add fill below to series override [#7916](https://github.com/grafana/grafana/issues/7916) +* **InfluxDB**: Influxb Datasource test passes even if the Database doesn't exist [#7864](https://github.com/grafana/grafana/issues/7864) +* **Prometheus**: Displaying Prometheus annotations is incredibly slow [#7750](https://github.com/grafana/grafana/issues/7750), thx [@mtanda](https://github.com/mtanda) +* **Graphite**: grafana generates empty find query to graphite -> 422 Unprocessable Entity [#7740](https://github.com/grafana/grafana/issues/7740) ## Changes * **Elasticsearch**: Changed elasticsearch Terms aggregation to default to Min Doc Count to 1, and sort order to Top [#8321](https://github.com/grafana/grafana/issues/8321) +## Tech + +* **Library Upgrade**: inconshreveable/log15 outdated - no support for solaris [#8262](https://github.com/grafana/grafana/issues/8262) +* **Library Upgrade**: Upgrade Macaron [#7600](https://github.com/grafana/grafana/issues/7600) + # 4.2.0 (2017-03-22) ## Minor Enhancements * **Templates**: Prevent use of the prefix `__` for templates in web UI [#7678](https://github.com/grafana/grafana/issues/7678) From f350ae242b3172b5966b5714d4ef9d7aec0ff857 Mon Sep 17 00:00:00 2001 From: Daniel Lee Date: Wed, 10 May 2017 14:32:18 +0200 Subject: [PATCH 56/85] Update CHANGELOG.md ref #7934 ref #7968 ref #3164 --- CHANGELOG.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bf13b4a51e7..27b1ff2659b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,16 +3,18 @@ ## Enhancements * **InfluxDB**: influxdb query builder support for ORDER BY and LIMIT (allows TOPN queries) [#6065](https://github.com/grafana/grafana/issues/6065) Support influxdb's SLIMIT Feature [#7232](https://github.com/grafana/grafana/issues/7232) thx [@thuck](https://github.com/thuck) -* **InfluxDB**: Small fix for the "glow" when focus the field for LIMIT and SLIMIT [#7799](https://github.com/grafana/grafana/pull/7799) thx [@thuck](https://github.com/thuck) * **Panels**: Delay loading & Lazy load panels as they become visible (scrolled into view) [#5216](https://github.com/grafana/grafana/issues/5216) thx [@jifwin](https://github.com/jifwin) * **Graph**: Support auto grid min/max when using log scale [#3090](https://github.com/grafana/grafana/issues/3090), thx [@bigbenhur](https://github.com/bigbenhur) * **Graph**: Support for histograms [#600](https://github.com/grafana/grafana/issues/600) * **Prometheus**: Support table response formats (column per label) [#6140](https://github.com/grafana/grafana/issues/6140), thx [@mtanda](https://github.com/mtanda) * **Single Stat Panel**: support for non time series data [#6564](https://github.com/grafana/grafana/issues/6564) * **Server**: Monitoring Grafana (health check endpoint) [#3302](https://github.com/grafana/grafana/issues/3302) +* **Heatmap**: Heatmap Panel [#7934](https://github.com/grafana/grafana/pull/7934) +* **Elasticsearch**: histogram aggregation [#3164](https://github.com/grafana/grafana/issues/3164) ## Minor Enchancements +* **InfluxDB**: Small fix for the "glow" when focus the field for LIMIT and SLIMIT [#7799](https://github.com/grafana/grafana/pull/7799) thx [@thuck](https://github.com/thuck) * **Prometheus**: Make Prometheus query field a textarea [#7663](https://github.com/grafana/grafana/issues/7663), thx [@hagen1778](https://github.com/hagen1778) * **Prometheus**: Step parameter changed semantics to min step to reduce the load on Prometheus and rendering in browser [#8073](https://github.com/grafana/grafana/pull/8073), thx [@bobrik](https://github.com/bobrik) * **Templating**: Should not be possible to create self-referencing (recursive) template variable definitions [#7614](https://github.com/grafana/grafana/issues/7614) thx [@thuck](https://github.com/thuck) @@ -32,6 +34,7 @@ * **Server**: Support listening on a UNIX socket [#4030](https://github.com/grafana/grafana/issues/4030), thx [@mitjaziv](https://github.com/mitjaziv) * **Graph**: Support log scaling for values smaller 1 [#5278](https://github.com/grafana/grafana/issues/5278) * **InfluxDB**: Slow 'select measurement' rendering for InfluxDB [#2524](https://github.com/grafana/grafana/issues/2524), thx [@sbhenderson](https://github.com/sbhenderson) +* **Config**: Configurable signout menu activation [#7968](https://github.com/grafana/grafana/pull/7968), thx [@seuf](https://github.com/seuf) ## Fixes * **Table Panel**: Fixed annotation display in table panel, [#8023](https://github.com/grafana/grafana/issues/8023) @@ -51,6 +54,7 @@ * **InfluxDB**: Influxb Datasource test passes even if the Database doesn't exist [#7864](https://github.com/grafana/grafana/issues/7864) * **Prometheus**: Displaying Prometheus annotations is incredibly slow [#7750](https://github.com/grafana/grafana/issues/7750), thx [@mtanda](https://github.com/mtanda) * **Graphite**: grafana generates empty find query to graphite -> 422 Unprocessable Entity [#7740](https://github.com/grafana/grafana/issues/7740) +* **Admin**: make organisation filter case insensitive [#8194](https://github.com/grafana/grafana/issues/8194), thx [@Alexander-N](https://github.com/Alexander-N) ## Changes * **Elasticsearch**: Changed elasticsearch Terms aggregation to default to Min Doc Count to 1, and sort order to Top [#8321](https://github.com/grafana/grafana/issues/8321) From 4a35126bf61cf7603159b221937981ddfe3d3aa6 Mon Sep 17 00:00:00 2001 From: Daniel Lee Date: Wed, 10 May 2017 15:23:59 +0200 Subject: [PATCH 57/85] api: health check returns 503 if db is failing ref #3302 --- pkg/api/http_server.go | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/pkg/api/http_server.go b/pkg/api/http_server.go index 06ef5a22125..1e143ef876f 100644 --- a/pkg/api/http_server.go +++ b/pkg/api/http_server.go @@ -189,11 +189,13 @@ func (hs *HttpServer) healthHandler(ctx *macaron.Context) { if err := bus.Dispatch(&models.GetDBHealthQuery{}); err != nil { data.Set("database", "failing") + ctx.Resp.Header().Set("Content-Type", "application/json; charset=UTF-8") + ctx.Resp.WriteHeader(503) + } else { + ctx.Resp.Header().Set("Content-Type", "application/json; charset=UTF-8") + ctx.Resp.WriteHeader(200) } - ctx.Resp.Header().Set("Content-Type", "application/json; charset=UTF-8") - ctx.Resp.WriteHeader(200) - dataBytes, _ := data.EncodePretty() ctx.Resp.Write(dataBytes) } From ee8799de889a71c852b341da395b2ffca6200851 Mon Sep 17 00:00:00 2001 From: Pranay Kanwar Date: Wed, 10 May 2017 19:16:19 +0530 Subject: [PATCH 58/85] Fix dropcounter option, is called dropResets (#8336) --- pkg/tsdb/opentsdb/opentsdb.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/tsdb/opentsdb/opentsdb.go b/pkg/tsdb/opentsdb/opentsdb.go index c0ba6603b20..987f341c7dc 100644 --- a/pkg/tsdb/opentsdb/opentsdb.go +++ b/pkg/tsdb/opentsdb/opentsdb.go @@ -191,7 +191,7 @@ func (e *OpenTsdbExecutor) buildMetric(query *tsdb.Query) map[string]interface{} } if !counterMaxCheck && (!resetValueCheck || resetValue.MustFloat64() == 0) { - rateOptions["dropcounter"] = true + rateOptions["dropResets"] = true } metric["rateOptions"] = rateOptions From d6eefcb5ceac0cab8bd9b90fe49c3231347e5809 Mon Sep 17 00:00:00 2001 From: Suzana Pescador Date: Wed, 10 May 2017 14:51:34 +0100 Subject: [PATCH 59/85] Can't remove default avg column in table #4515 (#8335) Avg column was being added at every rendering, if the table was empty. Now the column will be added once as an initialization when selecting a 'timeseries_aggregations' transform. --- public/app/plugins/panel/table/editor.ts | 4 ++++ public/app/plugins/panel/table/transformers.ts | 4 ---- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/public/app/plugins/panel/table/editor.ts b/public/app/plugins/panel/table/editor.ts index ce5d0c1c828..9850d60c9ad 100644 --- a/public/app/plugins/panel/table/editor.ts +++ b/public/app/plugins/panel/table/editor.ts @@ -53,6 +53,10 @@ export class TablePanelEditorCtrl { transformChanged() { this.panel.columns = []; + if (this.panel.transform === 'timeseries_aggregations') { + this.panel.columns.push({text: 'Avg', value: 'avg'}); + } + this.render(); } diff --git a/public/app/plugins/panel/table/transformers.ts b/public/app/plugins/panel/table/transformers.ts index c7f957e0156..07d183eafa8 100644 --- a/public/app/plugins/panel/table/transformers.ts +++ b/public/app/plugins/panel/table/transformers.ts @@ -89,10 +89,6 @@ transformers['timeseries_aggregations'] = { var i, y; model.columns.push({text: 'Metric'}); - if (panel.columns.length === 0) { - panel.columns.push({text: 'Avg', value: 'avg'}); - } - for (i = 0; i < panel.columns.length; i++) { model.columns.push({text: panel.columns[i].text}); } From 0a68dabb8942c5a0dcc306489891946ebf2a3179 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Wed, 10 May 2017 17:17:51 +0200 Subject: [PATCH 60/85] heatmap: Fixes & progress on heatmap docs --- docs/sources/features/panels/heatmap.md | 67 +++++++++++++++++++ .../panel/heatmap/heatmap_data_converter.ts | 2 +- .../plugins/panel/heatmap/heatmap_tooltip.ts | 1 - public/app/plugins/panel/heatmap/rendering.ts | 14 +--- public/sass/components/_panel_graph.scss | 2 +- 5 files changed, 72 insertions(+), 14 deletions(-) diff --git a/docs/sources/features/panels/heatmap.md b/docs/sources/features/panels/heatmap.md index dd469394cd0..b81df6278e3 100644 --- a/docs/sources/features/panels/heatmap.md +++ b/docs/sources/features/panels/heatmap.md @@ -14,3 +14,70 @@ weight = 3 ![](/img/docs/v43/heatmap_panel.png) +The Heatmap panel allows you to view histograms over time. + +## Histograms and buckets + +A histogram is a graphical representation of the distribution of numerical data. You group values into buckets +(some times also called bins) and then count how many values fall into each bucket. Instead +of graphing the actual values you then graph the buckets. Each each bar represents a bucket +and the bar height represents the frequency (i.e. count) of values that fell into that bucket's interval. + +Example Histogram: + +![](/img/docs/v43/heatmap_histogram.png) + +The above histogram shows us that most value distribution of a couple of time series. We can easily see that +most values land between 240-300 with a peak between 260-280. Histograms just look at value distributions +over specific time range. So you cannot see any trend or changes in the distribution over time, +this is where heatmaps become useful. + +## Heatmap + +A Heatmap is like a histogram but over time where each time slice represents it's own +histogram. Instead of using bar hight as a represenation of frequency you use a cells and color +the cell propotional to the number of values in the bucket. + +Example: + +![](/img/docs/v43/heatmap_histogram_over_time.png) + +Here we can clearly see what values are more common and how they trend over time. + +## Data Options + +Data and bucket options can be found in the `Axes` tab. + +### Data Formats + +Data format | Description +------------ | ------------- +*Time series* | Grafana does the bucketing by going through all time series values. The bucket sizes & intervals will be determined using the Buckets options. +*Time series buckets* | Each time series already represents a Y-Axis bucket. The time series name (alias) needs to be a numeric value representing the upper interval for the bucket. Grafana does no bucketing so the bucket size options are hidden. + +### Bucket Size + +The Bucket count & size options are used by Grafana to calculate how big each cell in the heatmap is. You can +define the bucket size either by count (the first input box) or by specifying a size interval. For the Y-Axis +the size interval is just a value but for the X-bucket you can specify a time range in the *Size* input, for example, +the time range `1h`. This will make the cells 1h wide on the X-axis. + +### Pre-bucketed data + +If you have a data that is already organized into buckets you can use the `Time series buckets` data format. This +format requires that your metric query return regular time series and that each time series has numeric name +that represent the upper or lower bound of the interval. + +The only data source that supports histograms over time is Elasticsearch. You do this by adding a *Histogram* +bucket aggregation before the *Date Histogram*. + +![](/img/docs/v43/elastic_histogram.png) + +You control the size of the buckets using the Histogram interval (Y-Axis) and the Date Histogram interval (X-axis). + +## Display Options + +The color spectrum controls what value get's assigned what color. The left most color on the +spectrum represents the low frequency and the color on the right most side represents the max frequency. +Most color schemes are automatically inverted when using the light theme. + diff --git a/public/app/plugins/panel/heatmap/heatmap_data_converter.ts b/public/app/plugins/panel/heatmap/heatmap_data_converter.ts index 0136a1bb511..e6ae9c701c1 100644 --- a/public/app/plugins/panel/heatmap/heatmap_data_converter.ts +++ b/public/app/plugins/panel/heatmap/heatmap_data_converter.ts @@ -38,7 +38,7 @@ function elasticHistogramToHeatmap(seriesList) { bucket = heatmap[time] = {x: time, buckets: {}}; } - bucket.buckets[bound] = {y: bound, count: count}; + bucket.buckets[bound] = {y: bound, count: count, values: [], points: []}; } } diff --git a/public/app/plugins/panel/heatmap/heatmap_tooltip.ts b/public/app/plugins/panel/heatmap/heatmap_tooltip.ts index d2152c68aa7..6d577ab9d37 100644 --- a/public/app/plugins/panel/heatmap/heatmap_tooltip.ts +++ b/public/app/plugins/panel/heatmap/heatmap_tooltip.ts @@ -126,7 +126,6 @@ export class HeatmapTooltip { getBucketIndexes(pos, data) { const xBucketIndex = this.getXBucketIndex(pos.offsetX, data); const yBucketIndex = this.getYBucketIndex(pos.offsetY, data); - return {xBucketIndex, yBucketIndex}; } diff --git a/public/app/plugins/panel/heatmap/rendering.ts b/public/app/plugins/panel/heatmap/rendering.ts index 12e7926f76f..d933888ea17 100644 --- a/public/app/plugins/panel/heatmap/rendering.ts +++ b/public/app/plugins/panel/heatmap/rendering.ts @@ -94,7 +94,7 @@ export default function link(scope, elem, attrs, ctrl) { } function addXAxis() { - xScale = d3.scaleTime() + scope.xScale = xScale = d3.scaleTime() .domain([timeRange.from, timeRange.to]) .range([0, chartWidth]); @@ -147,7 +147,7 @@ export default function link(scope, elem, attrs, ctrl) { ticks: ticks }; - yScale = d3.scaleLinear() + scope.yScale = yScale = d3.scaleLinear() .domain([y_min, y_max]) .range([chartHeight, 0]); @@ -206,7 +206,7 @@ export default function link(scope, elem, attrs, ctrl) { y_min = 1; } - yScale = d3.scaleLog() + scope.yScale = yScale = d3.scaleLog() .base(panel.yAxis.logBase) .domain([y_min, y_max]) .range([chartHeight, 0]); @@ -546,16 +546,10 @@ export default function link(scope, elem, attrs, ctrl) { // Shared crosshair and tooltip appEvents.on('graph-hover', event => { drawSharedCrosshair(event.pos); - - // Show shared tooltip - if (ctrl.dashboard.graphTooltip === 2) { - tooltip.show(event.pos, data); - } }, scope); appEvents.on('graph-hover-clear', () => { clearCrosshair(); - tooltip.destroy(); }, scope); function onMouseDown(event) { @@ -768,8 +762,6 @@ export default function link(scope, elem, attrs, ctrl) { } addHeatmap(); - scope.yScale = yScale; - scope.xScale = xScale; scope.yAxisWidth = yAxisWidth; scope.xAxisHeight = xAxisHeight; scope.chartHeight = chartHeight; diff --git a/public/sass/components/_panel_graph.scss b/public/sass/components/_panel_graph.scss index 179049ea220..11783692104 100644 --- a/public/sass/components/_panel_graph.scss +++ b/public/sass/components/_panel_graph.scss @@ -324,7 +324,7 @@ .axisLabel { color: $text-color; font-size: $font-size-sm; - position: absolute; + position: relative; text-align: center; font-size: 12px; } From 8c9cc4fae189433430968132e563789ee86c8c59 Mon Sep 17 00:00:00 2001 From: Suzana Pescador Date: Wed, 10 May 2017 19:02:35 +0100 Subject: [PATCH 61/85] [Bug] Coloring Background on siglestat panel #7242 (#8334) The bug was happening because the background color was being based on the rounded value (accounting the user defined decimals). Changed it to use the pure value (not the rounded) to follow whats being done in the value color, and also in the table thresholds (they don't consider the decimals when comparing to thresholds). --- public/app/plugins/panel/singlestat/module.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/public/app/plugins/panel/singlestat/module.ts b/public/app/plugins/panel/singlestat/module.ts index 406aedb3877..353786a1977 100644 --- a/public/app/plugins/panel/singlestat/module.ts +++ b/public/app/plugins/panel/singlestat/module.ts @@ -569,8 +569,8 @@ class SingleStatCtrl extends MetricsPanelCtrl { var body = panel.gauge.show ? '' : getBigValueHtml(); - if (panel.colorBackground && !isNaN(data.valueRounded)) { - var color = getColorForValue(data, data.valueRounded); + if (panel.colorBackground && !isNaN(data.value)) { + var color = getColorForValue(data, data.value); if (color) { $panelContainer.css('background-color', color); if (scope.fullscreen) { From b2c14b858ef135a6af231df1a028716de6667175 Mon Sep 17 00:00:00 2001 From: Tiantian Gao Date: Thu, 11 May 2017 14:53:40 +0800 Subject: [PATCH 62/85] Fix http logging `time_ms` unit is wrong (#8342) In fact, the unit of `time_ms` int http logging is not "ms", this patch fix it. --- pkg/middleware/logger.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/middleware/logger.go b/pkg/middleware/logger.go index 9bed7cbe16b..c3e5db66894 100644 --- a/pkg/middleware/logger.go +++ b/pkg/middleware/logger.go @@ -49,9 +49,9 @@ func Logger() macaron.Handler { if ctx, ok := c.Data["ctx"]; ok { ctxTyped := ctx.(*Context) if status == 500 { - ctxTyped.Logger.Error("Request Completed", "method", req.Method, "path", req.URL.Path, "status", status, "remote_addr", c.RemoteAddr(), "time_ms", timeTakenMs, "size", rw.Size()) + ctxTyped.Logger.Error("Request Completed", "method", req.Method, "path", req.URL.Path, "status", status, "remote_addr", c.RemoteAddr(), "time_ms", int64(timeTakenMs), "size", rw.Size()) } else { - ctxTyped.Logger.Info("Request Completed", "method", req.Method, "path", req.URL.Path, "status", status, "remote_addr", c.RemoteAddr(), "time_ms", timeTakenMs, "size", rw.Size()) + ctxTyped.Logger.Info("Request Completed", "method", req.Method, "path", req.URL.Path, "status", status, "remote_addr", c.RemoteAddr(), "time_ms", int64(timeTakenMs), "size", rw.Size()) } } } From 30b6c3b54a4e7315982df9f2294eb1b596447056 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Thu, 11 May 2017 08:52:22 +0200 Subject: [PATCH 63/85] build: fixed heatmap test --- .../heatmap/specs/heatmap_data_converter_specs.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/public/app/plugins/panel/heatmap/specs/heatmap_data_converter_specs.ts b/public/app/plugins/panel/heatmap/specs/heatmap_data_converter_specs.ts index 7c8dce2b822..03adf1f9fa3 100644 --- a/public/app/plugins/panel/heatmap/specs/heatmap_data_converter_specs.ts +++ b/public/app/plugins/panel/heatmap/specs/heatmap_data_converter_specs.ts @@ -223,17 +223,17 @@ describe('ES Histogram converter', () => { '1422774000000': { x: 1422774000000, buckets: { - '1': { y: 1, count: 1 }, - '2': { y: 2, count: 5 }, - '3': { y: 3, count: 0 } + '1': { y: 1, count: 1, values: [], points: [] }, + '2': { y: 2, count: 5, values: [], points: [] }, + '3': { y: 3, count: 0, values: [], points: [] } } }, '1422774060000': { x: 1422774060000, buckets: { - '1': { y: 1, count: 0 }, - '2': { y: 2, count: 3 }, - '3': { y: 3, count: 1 } + '1': { y: 1, count: 0, values: [], points: [] }, + '2': { y: 2, count: 3, values: [], points: [] }, + '3': { y: 3, count: 1, values: [], points: [] } } }, }; From ab6740c6856b001dde6f2cd3a6bf302d1e5d2fa9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Thu, 11 May 2017 10:19:47 +0200 Subject: [PATCH 64/85] heatmap: Docs and heatmap fixes --- docs/sources/features/panels/heatmap.md | 40 ++++++++++---- public/app/plugins/panel/graph/graph.ts | 2 +- .../app/plugins/panel/heatmap/heatmap_ctrl.ts | 1 - .../panel/heatmap/heatmap_data_converter.ts | 19 ------- public/app/plugins/panel/heatmap/rendering.ts | 53 +++++++++---------- 5 files changed, 57 insertions(+), 58 deletions(-) diff --git a/docs/sources/features/panels/heatmap.md b/docs/sources/features/panels/heatmap.md index b81df6278e3..c2f1c0b93bc 100644 --- a/docs/sources/features/panels/heatmap.md +++ b/docs/sources/features/panels/heatmap.md @@ -20,7 +20,7 @@ The Heatmap panel allows you to view histograms over time. A histogram is a graphical representation of the distribution of numerical data. You group values into buckets (some times also called bins) and then count how many values fall into each bucket. Instead -of graphing the actual values you then graph the buckets. Each each bar represents a bucket +of graphing the actual values you then graph the buckets. Each bar represents a bucket and the bar height represents the frequency (i.e. count) of values that fell into that bucket's interval. Example Histogram: @@ -34,9 +34,9 @@ this is where heatmaps become useful. ## Heatmap -A Heatmap is like a histogram but over time where each time slice represents it's own -histogram. Instead of using bar hight as a represenation of frequency you use a cells and color -the cell propotional to the number of values in the bucket. +A Heatmap is like a histogram but over time where each time slice represents its own +histogram. Instead of using bar height as a representation of frequency you use cells and color +the cell proportional to the number of values in the bucket. Example: @@ -64,8 +64,7 @@ the time range `1h`. This will make the cells 1h wide on the X-axis. ### Pre-bucketed data -If you have a data that is already organized into buckets you can use the `Time series buckets` data format. This -format requires that your metric query return regular time series and that each time series has numeric name +If you have a data that is already organized into buckets you can use the `Time series buckets` data format. This format requires that your metric query return regular time series and that each time series has a numeric name that represent the upper or lower bound of the interval. The only data source that supports histograms over time is Elasticsearch. You do this by adding a *Histogram* @@ -77,7 +76,30 @@ You control the size of the buckets using the Histogram interval (Y-Axis) and th ## Display Options -The color spectrum controls what value get's assigned what color. The left most color on the -spectrum represents the low frequency and the color on the right most side represents the max frequency. -Most color schemes are automatically inverted when using the light theme. +In the heatmap *Display* tab you define how the cells are rendered and what color they are assigned. +### Color Mode & Spectrum + +{{< imgbox max-width="40%" img="/img/docs/v43/heatmap_scheme.png" caption="Color spectrum" >}} + +The color spectrum controls the mapping between value count (in each bucket) and the color assigned to each bucket. +The left most color on the spectrum represents the minimum count and the color on the right most side represents the +maximum count. Some color schemes are automatically inverted when using the light theme. + +You can also change the color mode to `Opacity`. In this case, the color will not change but the amount of opacity will +change with the bucket count. + +## Raw data vs aggregated + +If you use the heatmap with regular time series data (not pre-bucketed). Then it's important to keep in mind that your data +is often already by aggregated by your time series backend. Most time series queries do not return raw sample data +but include a group by time interval or maxDataPoints limit coupled with an aggregation function (usually average). + +This all depends on the time range of your query of course. But the important point is to know that the Histogram bucketing +that Grafana performs may be done on already aggregated and averaged data. To get more accurate heatmaps it is better +to do the bucketing during metric collection or store the data in Elasticsearch, which currently is the only data source +data supports doing Histogram bucketing on the raw data. + +If you remove or lower the group by time (or raise maxDataPoints) in your query to return more data points your heatmap will be +more accurate but this can also be very CPU & Memory taxing for your browser and could cause hangs and crashes if the number of +data points becomes unreasonably large. diff --git a/public/app/plugins/panel/graph/graph.ts b/public/app/plugins/panel/graph/graph.ts index 3d4fd4c7edb..75b5f8615ac 100755 --- a/public/app/plugins/panel/graph/graph.ts +++ b/public/app/plugins/panel/graph/graph.ts @@ -423,7 +423,7 @@ coreModule.directive('grafanaGraph', function($rootScope, timeSrv, popoverSrv) { function addXHistogramAxis(options, bucketSize) { let ticks, min, max; - if (data.length) { + if (data.length && bucketSize) { ticks = _.map(data[0].data, point => point[0]); // Expand ticks for pretty view diff --git a/public/app/plugins/panel/heatmap/heatmap_ctrl.ts b/public/app/plugins/panel/heatmap/heatmap_ctrl.ts index 02d2ef24b79..9d770f21e95 100644 --- a/public/app/plugins/panel/heatmap/heatmap_ctrl.ts +++ b/public/app/plugins/panel/heatmap/heatmap_ctrl.ts @@ -38,7 +38,6 @@ let panelDefaults = { splitFactor: null, min: null, max: null, - removeZeroValues: false }, xBucketSize: null, xBucketNumber: null, diff --git a/public/app/plugins/panel/heatmap/heatmap_data_converter.ts b/public/app/plugins/panel/heatmap/heatmap_data_converter.ts index e6ae9c701c1..f7e32f3df7c 100644 --- a/public/app/plugins/panel/heatmap/heatmap_data_converter.ts +++ b/public/app/plugins/panel/heatmap/heatmap_data_converter.ts @@ -122,24 +122,6 @@ function mergeZeroBuckets(buckets, minValue) { return buckets; } -/** - * Remove 0 values from heatmap buckets. - */ -function removeZeroBuckets(buckets) { - _.forEach(buckets, xBucket => { - let yBuckets = xBucket.buckets; - let newYBuckets = {}; - _.forEach(yBuckets, (bucket, bound) => { - if (bucket.y !== 0) { - newYBuckets[bound] = bucket; - } - }); - xBucket.buckets = newYBuckets; - }); - - return buckets; -} - /** * Convert set of time series into heatmap buckets * @return {Object} Heatmap object: @@ -429,7 +411,6 @@ export { convertToHeatMap, elasticHistogramToHeatmap, convertToCards, - removeZeroBuckets, mergeZeroBuckets, getMinLog, getValueBucketBound, diff --git a/public/app/plugins/panel/heatmap/rendering.ts b/public/app/plugins/panel/heatmap/rendering.ts index d933888ea17..085d9bd2673 100644 --- a/public/app/plugins/panel/heatmap/rendering.ts +++ b/public/app/plugins/panel/heatmap/rendering.ts @@ -8,7 +8,7 @@ import {appEvents, contextSrv} from 'app/core/core'; import {tickStep} from 'app/core/utils/ticks'; import d3 from 'd3'; import {HeatmapTooltip} from './heatmap_tooltip'; -import {convertToCards, mergeZeroBuckets, removeZeroBuckets} from './heatmap_data_converter'; +import {convertToCards, mergeZeroBuckets} from './heatmap_data_converter'; let MIN_CARD_SIZE = 1, CARD_PADDING = 1, @@ -357,14 +357,10 @@ export default function link(scope, elem, attrs, ctrl) { addAxes(); if (panel.yAxis.logBase !== 1) { - if (panel.yAxis.removeZeroValues) { - data.buckets = removeZeroBuckets(data.buckets); - } else { - let log_base = panel.yAxis.logBase; - let domain = yScale.domain(); - let tick_values = logScaleTickValues(domain, log_base); - data.buckets = mergeZeroBuckets(data.buckets, _.min(tick_values)); - } + let log_base = panel.yAxis.logBase; + let domain = yScale.domain(); + let tick_values = logScaleTickValues(domain, log_base); + data.buckets = mergeZeroBuckets(data.buckets, _.min(tick_values)); } let cardsData = convertToCards(data.buckets); @@ -377,17 +373,17 @@ export default function link(scope, elem, attrs, ctrl) { let cards = heatmap.selectAll(".heatmap-card").data(cardsData); cards.append("title"); cards = cards.enter().append("rect") - .attr("x", getCardX) - .attr("width", getCardWidth) - .attr("y", getCardY) - .attr("height", getCardHeight) - .attr("rx", cardRound) - .attr("ry", cardRound) - .attr("class", "bordered heatmap-card") - .style("fill", getCardColor) - .style("stroke", getCardColor) - .style("stroke-width", 0) - .style("opacity", getCardOpacity); + .attr("x", getCardX) + .attr("width", getCardWidth) + .attr("y", getCardY) + .attr("height", getCardHeight) + .attr("rx", cardRound) + .attr("ry", cardRound) + .attr("class", "bordered heatmap-card") + .style("fill", getCardColor) + .style("stroke", getCardColor) + .style("stroke-width", 0) + .style("opacity", getCardOpacity); let $cards = $heatmap.find(".heatmap-card"); $cards.on("mouseenter", (event) => { @@ -750,6 +746,15 @@ export default function link(scope, elem, attrs, ctrl) { panel = ctrl.panel; timeRange = ctrl.range; + // Draw only if color editor is opened + if (!d3.select("#heatmap-color-legend").empty()) { + drawColorLegend(); + } + + if (!d3.select("#heatmap-opacity-legend").empty()) { + drawOpacityLegend(); + } + if (!setElementHeight() || !data) { return; } @@ -767,14 +772,6 @@ export default function link(scope, elem, attrs, ctrl) { scope.chartHeight = chartHeight; scope.chartWidth = chartWidth; scope.chartTop = chartTop; - - // Draw only if color editor is opened - if (!d3.select("#heatmap-color-legend").empty()) { - drawColorLegend(); - } - if (!d3.select("#heatmap-opacity-legend").empty()) { - drawOpacityLegend(); - } } // Register selection listeners From 4ce0bf4d16b9526bbca34b918929bc4d7a136d77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Thu, 11 May 2017 10:50:24 +0200 Subject: [PATCH 65/85] mysql: improved mysql data source, added test feature when adding data source, fixed cache issue --- pkg/api/datasources.go | 4 ++- pkg/models/datasource.go | 5 ++-- pkg/services/sqlstore/datasource.go | 1 + .../plugins/datasource/mysql/datasource.ts | 28 +++++++++++++++++++ 4 files changed, 35 insertions(+), 3 deletions(-) diff --git a/pkg/api/datasources.go b/pkg/api/datasources.go index 1d99dac3d40..9ffdc4a6d1b 100644 --- a/pkg/api/datasources.go +++ b/pkg/api/datasources.go @@ -149,8 +149,8 @@ func fillWithSecureJsonData(cmd *m.UpdateDataSourceCommand) error { if err != nil { return err } - secureJsonData := ds.SecureJsonData.Decrypt() + secureJsonData := ds.SecureJsonData.Decrypt() for k, v := range secureJsonData { if _, ok := cmd.SecureJsonData[k]; !ok { @@ -158,6 +158,8 @@ func fillWithSecureJsonData(cmd *m.UpdateDataSourceCommand) error { } } + // set version from db + cmd.Version = ds.Version return nil } diff --git a/pkg/models/datasource.go b/pkg/models/datasource.go index 804880a5d10..3fdfd9c47da 100644 --- a/pkg/models/datasource.go +++ b/pkg/models/datasource.go @@ -116,8 +116,9 @@ type UpdateDataSourceCommand struct { JsonData *simplejson.Json `json:"jsonData"` SecureJsonData map[string]string `json:"secureJsonData"` - OrgId int64 `json:"-"` - Id int64 `json:"-"` + OrgId int64 `json:"-"` + Id int64 `json:"-"` + Version int `json:"-"` } type DeleteDataSourceByIdCommand struct { diff --git a/pkg/services/sqlstore/datasource.go b/pkg/services/sqlstore/datasource.go index de838163681..625b53469e9 100644 --- a/pkg/services/sqlstore/datasource.go +++ b/pkg/services/sqlstore/datasource.go @@ -141,6 +141,7 @@ func UpdateDataSource(cmd *m.UpdateDataSourceCommand) error { JsonData: cmd.JsonData, SecureJsonData: securejsondata.GetEncryptedJsonData(cmd.SecureJsonData), Updated: time.Now(), + Version: cmd.Version + 1, } sess.UseBool("is_default") diff --git a/public/app/plugins/datasource/mysql/datasource.ts b/public/app/plugins/datasource/mysql/datasource.ts index f798ce0e838..2f2eaec395d 100644 --- a/public/app/plugins/datasource/mysql/datasource.ts +++ b/public/app/plugins/datasource/mysql/datasource.ts @@ -52,6 +52,34 @@ export class MysqlDatasource { }).then(this.processQueryResult.bind(this)); } + testDatasource() { + return this.backendSrv.datasourceRequest({ + url: '/api/tsdb/query', + method: 'POST', + data: { + from: '5m', + to: 'now', + queries: [{ + refId: 'A', + intervalMs: 1, + maxDataPoints: 1, + datasourceId: this.id, + rawSql: "SELECT 1", + format: 'table', + }], + } + }).then(res => { + return { status: "success", message: "Database Connection OK", title: "Success" }; + }).catch(err => { + console.log(err); + if (err.data && err.data.message) { + return { status: "error", message: err.data.message, title: "Error" }; + } else { + return { status: "error", message: err.status, title: "Error" }; + } + }); + } + processQueryResult(res) { var data = []; From f976e465c47f7c8141fd8f2815eb4cfa4e9e431c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Thu, 11 May 2017 11:06:13 +0200 Subject: [PATCH 66/85] mysql: minor improvement for table panel --- public/app/plugins/datasource/mysql/query_ctrl.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/public/app/plugins/datasource/mysql/query_ctrl.ts b/public/app/plugins/datasource/mysql/query_ctrl.ts index 17242fac1f0..889855a343a 100644 --- a/public/app/plugins/datasource/mysql/query_ctrl.ts +++ b/public/app/plugins/datasource/mysql/query_ctrl.ts @@ -48,7 +48,14 @@ export class MysqlQueryCtrl extends QueryCtrl { ]; if (!this.target.rawSql) { - this.target.rawSql = defaulQuery; + + // special handling when in table panel + if (this.panelCtrl.panel.type === 'table') { + this.target.format = 'table'; + this.target.rawSql = "SELECT 1"; + } else { + this.target.rawSql = defaulQuery; + } } this.panelCtrl.events.on('data-received', this.onDataReceived.bind(this), $scope); From 2c5563442f2d2f2d1410a53846c63f342565e45c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Thu, 11 May 2017 12:59:49 +0200 Subject: [PATCH 67/85] docs: added initial mysql docs --- docs/sources/features/datasources/mysql.md | 94 ++++++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100644 docs/sources/features/datasources/mysql.md diff --git a/docs/sources/features/datasources/mysql.md b/docs/sources/features/datasources/mysql.md new file mode 100644 index 00000000000..dc4e68c0ed4 --- /dev/null +++ b/docs/sources/features/datasources/mysql.md @@ -0,0 +1,94 @@ ++++ +title = "Using MySQL in Grafana" +description = "Guide for using MySQL in Grafana" +keywords = ["grafana", "mysql", "guide"] +type = "docs" +[menu.docs] +name = "MySQL" +parent = "datasources" +weight = 7 ++++ + +# Using MySQL in Grafana + +> Only available in Grafana v4.3+. This data source is not ready for +> production use, currently in development (alpha state). + +Grafana ships with a built-in MySQL data source plugin that allow you to query any visualize +data from MySQL compatible database. + +## Macros + +To simplify syntax and to allow for dynamic parts, like date range filters, the query can contain macros. + +Macro example | Description +------------ | ------------- +*__timeFilter(dateColumn)* | Will be replaced by a time range filter using the specified column name. For example, *dateColumn > FROM_UNIXTIME(1494410783) AND dateColumn < FROM_UNIXTIME(1494497183)* + +We plan to add many more macros. If you have suggestions for what macros you would like to see, please +[open an issue](https://github.com/grafana/grafana) in our GitHub repo. + +The query editor has a link named `Generated SQL` that show up after a query as been executed, while in panel edit mode. Click +on it and it will expand and show the raw interpolated SQL string that was executed. + +## Table queries + +If the `Format as` query option is set to `Table` then you can basically do any type of SQL query. The table panel will automatically show the results of whatever columns & rows your query returns. + +Query editor with example query: + +![](/img/docs/v43/mysql_table_query.png) + + +The query: + +```sql +SELECT + title as 'Title', + user.login as 'Created By' , + dashboard.created as 'Created On' + FROM dashboard +INNER JOIN user on user.id = dashboard.created_by +WHERE $__timeFilter(dashboard.created) +``` + +You can control the name of the Table panel columns by using regular `as ` SQL column selection syntax. + +The resulting table panel: + +![](/img/docs/v43/mysql_table.png) + +### Time series queries + +If you set `Format as` to `Time series`, for use in Graph panel for example, then there are some requirements for +what your query returns. + +- Must be a column named `time_sec` representing a unix epoch in seconds. +- Must be a column named `value` representing the time series value. +- Must be a column named `metric` representing the time series name. + +Example: + +```sql +SELECT + min(UNIX_TIMESTAMP(time_date_time)) as time_sec, + max(value_double) as value, + metric1 as metric +FROM test_data +WHERE $__timeFilter(time_date_time) +GROUP BY metric1, UNIX_TIMESTAMP(time_date_time) DIV 300 +ORDER BY time_sec asc +``` + +Currently, there is no support for a dynamic group by time based on time range & panel width. +This is something we plan to add. + +## Templating + +You can use variables in your queries but there are currently no support for defining `Query` variables +that target a MySQL data source. + +## Alerting + +Time series queries should work in alerting conditions. Table formatted queries is not yet supported in alert rule +conditions. From 2c3f3dffa30e1b2625b1c167a2dec2ac531bc30a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Thu, 11 May 2017 13:02:10 +0200 Subject: [PATCH 68/85] docs: updated heatmap panel docs menu name --- docs/sources/features/panels/heatmap.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/sources/features/panels/heatmap.md b/docs/sources/features/panels/heatmap.md index c2f1c0b93bc..ef4aadf5529 100644 --- a/docs/sources/features/panels/heatmap.md +++ b/docs/sources/features/panels/heatmap.md @@ -4,6 +4,7 @@ description = "Heatmap panel documentation" keywords = ["grafana", "heatmap", "panel", "documentation"] type = "docs" [menu.docs] +name = "Heatmap" parent = "panels" weight = 3 +++ @@ -102,4 +103,4 @@ data supports doing Histogram bucketing on the raw data. If you remove or lower the group by time (or raise maxDataPoints) in your query to return more data points your heatmap will be more accurate but this can also be very CPU & Memory taxing for your browser and could cause hangs and crashes if the number of -data points becomes unreasonably large. +data points becomes unreasonably large. From 88672389f35df7b2afa4d3394434b6cd468764f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Thu, 11 May 2017 14:16:05 +0200 Subject: [PATCH 69/85] docs: minor docs fix --- docs/sources/features/datasources/mysql.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/sources/features/datasources/mysql.md b/docs/sources/features/datasources/mysql.md index dc4e68c0ed4..762a9329347 100644 --- a/docs/sources/features/datasources/mysql.md +++ b/docs/sources/features/datasources/mysql.md @@ -15,7 +15,7 @@ weight = 7 > production use, currently in development (alpha state). Grafana ships with a built-in MySQL data source plugin that allow you to query any visualize -data from MySQL compatible database. +data from a MySQL compatible database. ## Macros @@ -23,7 +23,7 @@ To simplify syntax and to allow for dynamic parts, like date range filters, the Macro example | Description ------------ | ------------- -*__timeFilter(dateColumn)* | Will be replaced by a time range filter using the specified column name. For example, *dateColumn > FROM_UNIXTIME(1494410783) AND dateColumn < FROM_UNIXTIME(1494497183)* +*$__timeFilter(dateColumn)* | Will be replaced by a time range filter using the specified column name. For example, *dateColumn > FROM_UNIXTIME(1494410783) AND dateColumn < FROM_UNIXTIME(1494497183)* We plan to add many more macros. If you have suggestions for what macros you would like to see, please [open an issue](https://github.com/grafana/grafana) in our GitHub repo. From 5e85558e9eb54466a796f440a05302101e4be2bf Mon Sep 17 00:00:00 2001 From: Mitsuhiro Tanda Date: Thu, 11 May 2017 21:24:13 +0900 Subject: [PATCH 70/85] (prometheus) fix graph link (#8349) --- .../plugins/datasource/prometheus/query_ctrl.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/public/app/plugins/datasource/prometheus/query_ctrl.ts b/public/app/plugins/datasource/prometheus/query_ctrl.ts index 09ea5e6aeb3..7d94a006998 100644 --- a/public/app/plugins/datasource/prometheus/query_ctrl.ts +++ b/public/app/plugins/datasource/prometheus/query_ctrl.ts @@ -78,15 +78,15 @@ class PrometheusQueryCtrl extends QueryCtrl { var rangeDiff = Math.ceil((range.to.valueOf() - range.from.valueOf()) / 1000); var endTime = range.to.utc().format('YYYY-MM-DD HH:mm'); var expr = { - expr: this.templateSrv.replace(this.target.expr, this.panelCtrl.panel.scopedVars, this.datasource.interpolateQueryExpr), - range_input: rangeDiff + 's', - end_input: endTime, - step_input: this.target.step, - stacked: this.panelCtrl.panel.stack, - tab: 0 + 'g0.expr': this.templateSrv.replace(this.target.expr, this.panelCtrl.panel.scopedVars, this.datasource.interpolateQueryExpr), + 'g0.range_input': rangeDiff + 's', + 'g0.end_input': endTime, + 'g0.step_input': this.target.step, + 'g0.stacked': this.panelCtrl.panel.stack ? 1 : 0, + 'g0.tab': 0 }; - var hash = encodeURIComponent(JSON.stringify([expr])); - this.linkToPrometheus = this.datasource.directUrl + '/graph#' + hash; + var args = _.map(expr, (v, k) => { return k + '=' + encodeURIComponent(v); }).join('&'); + this.linkToPrometheus = this.datasource.directUrl + '/graph?' + args; } getCollapsedText() { From 2e7ac8f2da3014170fb16b6d070f787e775c7796 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Thu, 11 May 2017 15:20:13 +0200 Subject: [PATCH 71/85] heatmap: fix for unit options width --- .../plugins/panel/heatmap/partials/axes_editor.html | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/public/app/plugins/panel/heatmap/partials/axes_editor.html b/public/app/plugins/panel/heatmap/partials/axes_editor.html index b60e21eab85..10af1977af7 100644 --- a/public/app/plugins/panel/heatmap/partials/axes_editor.html +++ b/public/app/plugins/panel/heatmap/partials/axes_editor.html @@ -3,7 +3,7 @@
    Y Axis
    -
    @@ -11,21 +11,21 @@
    -
    +
    - +
    - +
    -
    From 5076460254828f5ac70bcf599034ca454802d9a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Thu, 11 May 2017 16:30:50 +0200 Subject: [PATCH 72/85] docs: updated heatmap docs --- docs/sources/features/panels/heatmap.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/sources/features/panels/heatmap.md b/docs/sources/features/panels/heatmap.md index ef4aadf5529..768585c8105 100644 --- a/docs/sources/features/panels/heatmap.md +++ b/docs/sources/features/panels/heatmap.md @@ -15,7 +15,9 @@ weight = 3 ![](/img/docs/v43/heatmap_panel.png) -The Heatmap panel allows you to view histograms over time. +The Heatmap panel allows you to view histograms over time. To fully understand and use this panel you need +understand what Histograms are and how they are created. Read on below to for a quick introduction to the +term Histogram. ## Histograms and buckets From 17198807c9fbaf465062dbf8cc6cc70fb46e0aeb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Fri, 12 May 2017 11:04:54 +0200 Subject: [PATCH 73/85] docs: updated mysql docs --- docs/sources/features/datasources/mysql.md | 24 ++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/docs/sources/features/datasources/mysql.md b/docs/sources/features/datasources/mysql.md index 762a9329347..2072e6a4f7b 100644 --- a/docs/sources/features/datasources/mysql.md +++ b/docs/sources/features/datasources/mysql.md @@ -17,6 +17,30 @@ weight = 7 Grafana ships with a built-in MySQL data source plugin that allow you to query any visualize data from a MySQL compatible database. +## Adding the data source + +1. Open the side menu by clicking the Grafana icon in the top header. +2. In the side menu under the `Dashboards` link you should find a link named `Data Sources`. +3. Click the `+ Add data source` button in the top header. +4. Select *MySQL* from the *Type* dropdown. + +### Database User Permissions (Important!) + +The database user you specify when you add the data source should only be granted SELECT permissions on +the specified database & tables you want to query. Grafana does not validate that the query is safe. The query +could include any SQL statement. For example, statements like `USE otherdb;` and `DROP TABLE user;` would be +executed. To protect against this we **Highly** recommmend you create a specific mysql user with +restricted permissions. + +Example: + +```sql + CREATE USER 'grafanaReader' IDENTIFIED BY 'password'; + GRANT SELECT ON mydatabase.mytable TO 'grafanaReader'; +``` + +You can use wildcards (`*`) in place of database or table if you want to grant access to more databases and tables. + ## Macros To simplify syntax and to allow for dynamic parts, like date range filters, the query can contain macros. From 375e60750e3e35e9b2d8c7592c3d22e6b1de1593 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Fri, 12 May 2017 11:14:11 +0200 Subject: [PATCH 74/85] docs: updated heatmap docs --- docs/sources/features/panels/heatmap.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/sources/features/panels/heatmap.md b/docs/sources/features/panels/heatmap.md index 768585c8105..e44527f8695 100644 --- a/docs/sources/features/panels/heatmap.md +++ b/docs/sources/features/panels/heatmap.md @@ -11,9 +11,9 @@ weight = 3 # Heatmap Panel -> New panel only available in Grafana v4.3+ +![](/img/docs/v43/heatmap_panel_cover.jpg) -![](/img/docs/v43/heatmap_panel.png) +> New panel only available in Grafana v4.3+ The Heatmap panel allows you to view histograms over time. To fully understand and use this panel you need understand what Histograms are and how they are created. Read on below to for a quick introduction to the From a9c535e551d9508b18a350f774acc98644111ff8 Mon Sep 17 00:00:00 2001 From: Daniel Lee Date: Fri, 12 May 2017 11:37:23 +0200 Subject: [PATCH 75/85] mysql: add some more type mapping Decimals mapped to floats for now. No mapping for bit or any of the blob types. Tinyint not mapped to bool. --- pkg/tsdb/mysql/mysql.go | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/pkg/tsdb/mysql/mysql.go b/pkg/tsdb/mysql/mysql.go index 705db1e50da..4ebe467459d 100644 --- a/pkg/tsdb/mysql/mysql.go +++ b/pkg/tsdb/mysql/mysql.go @@ -184,14 +184,34 @@ func (e MysqlExecutor) getTypedRowData(types []*sql.ColumnType, rows *core.Rows) for i, stype := range types { switch stype.DatabaseTypeName() { + case mysql.FieldTypeNameTiny: + values[i] = new(int8) + case mysql.FieldTypeNameInt24: + values[i] = new(int32) + case mysql.FieldTypeNameShort: + values[i] = new(int16) case mysql.FieldTypeNameVarString: values[i] = new(string) + case mysql.FieldTypeNameVarChar: + values[i] = new(string) case mysql.FieldTypeNameLongLong: values[i] = new(int64) case mysql.FieldTypeNameDouble: values[i] = new(float64) + case mysql.FieldTypeNameDecimal: + values[i] = new(float32) + case mysql.FieldTypeNameNewDecimal: + values[i] = new(float64) + case mysql.FieldTypeNameTimestamp: + values[i] = new(time.Time) case mysql.FieldTypeNameDateTime: values[i] = new(time.Time) + case mysql.FieldTypeNameTime: + values[i] = new(time.Duration) + case mysql.FieldTypeNameYear: + values[i] = new(int16) + case mysql.FieldTypeNameNULL: + values[i] = nil default: return nil, fmt.Errorf("Database type %s not supported", stype.DatabaseTypeName()) } From 3a892727b3cf893b87e51d033fa57d7392e41ad4 Mon Sep 17 00:00:00 2001 From: Daniel Lee Date: Fri, 12 May 2017 11:45:26 +0200 Subject: [PATCH 76/85] release version bump 4.3.0-beta1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index c72b93ddd1a..47102820d58 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "company": "Coding Instinct AB" }, "name": "grafana", - "version": "4.3.0-pre1", + "version": "4.3.0-beta1", "repository": { "type": "git", "url": "http://github.com/grafana/grafana.git" From a73f664d546a6fab8de1e2f6c5060c40208d9e7f Mon Sep 17 00:00:00 2001 From: Daniel Lee Date: Fri, 12 May 2017 13:26:34 +0200 Subject: [PATCH 77/85] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 27b1ff2659b..f80ebcc1381 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -# 4.3.0 (unreleased) +# 4.3.0-beta1 (2017-05-12) ## Enhancements From 88d4fde81598b8ea1d1e1b3c405dd8c6a0b785e3 Mon Sep 17 00:00:00 2001 From: Daniel Lee Date: Fri, 12 May 2017 13:35:52 +0200 Subject: [PATCH 78/85] docs: v4.3.0-beta1 updates --- README.md | 1 + docs/sources/guides/whats-new-in-v4-2.md | 2 +- docs/sources/guides/whats-new-in-v4-3.md | 105 +++++++++++++++++++++++ docs/sources/installation/debian.md | 9 ++ docs/sources/installation/rpm.md | 1 + docs/sources/installation/windows.md | 1 + 6 files changed, 118 insertions(+), 1 deletion(-) create mode 100644 docs/sources/guides/whats-new-in-v4-3.md diff --git a/README.md b/README.md index 9b5b02dec54..0f658f72d68 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,7 @@ Graphite, Elasticsearch, OpenTSDB, Prometheus and InfluxDB. - [What's New in Grafana 4.0](http://docs.grafana.org/guides/whats-new-in-v4/) - [What's New in Grafana 4.1](http://docs.grafana.org/guides/whats-new-in-v4-1/) - [What's New in Grafana 4.2](http://docs.grafana.org/guides/whats-new-in-v4-2/) +- [What's New in Grafana 4.3](http://docs.grafana.org/guides/whats-new-in-v4-3/) ## Features diff --git a/docs/sources/guides/whats-new-in-v4-2.md b/docs/sources/guides/whats-new-in-v4-2.md index 44aa3a45dc0..4b140a9027e 100644 --- a/docs/sources/guides/whats-new-in-v4-2.md +++ b/docs/sources/guides/whats-new-in-v4-2.md @@ -12,7 +12,7 @@ weight = -1 ## Whats new in Grafana v4.2 -Grafana v4.2 Beta is now [available for download](/download/4_2_0/). +Grafana v4.2 Beta is now [available for download](https://grafana.com/grafana/download/4.2.0). Just like the last release this one contains lots bug fixes and minor improvements. We are very happy to say that 27 of 40 issues was closed by pull requests from the community. Big thumbs up! diff --git a/docs/sources/guides/whats-new-in-v4-3.md b/docs/sources/guides/whats-new-in-v4-3.md new file mode 100644 index 00000000000..ef37c56bfa0 --- /dev/null +++ b/docs/sources/guides/whats-new-in-v4-3.md @@ -0,0 +1,105 @@ ++++ +title = "What's New in Grafana v4.3" +description = "Feature & improvement highlights for Grafana v4.3" +keywords = ["grafana", "new", "documentation", "4.3.0"] +type = "docs" +[menu.docs] +name = "Version 4.3" +identifier = "v4.3" +parent = "whatsnew" +weight = -1 ++++ + +## What's New in Grafana v4.3 + +Grafana v4.3 Beta is now [available for download](https://grafana.com/grafana/download/4.3.0-beta1). + +## Release Highlights + +- New [Heatmap Panel](http://docs.grafana.org/features/panels/heatmap/) +- Graph Panel Histogram Mode +- Elasticsearch Histogram Aggregation +- Prometheus Table data format +- New [MySQL Data Source](http://docs.grafana.org/features/datasources/mysql/) (alpha version to get some early feedback) +- 60+ small fixes and improvements, most of them contributed by our fantastic community! + +Check out the [New Features in v4.3 Dashboard](http://play.grafana.org/dashboard/db/new-features-in-v4-3?orgId=1) on the Grafana Play site for a showcase of these new features. + +## Histogram Support + +A Histogram is a kind of bar chart that groups numbers into ranges, often called buckets or bins. Taller bars show that more data falls in that range. + +The Graph Panel now supports Histograms. + +![](/img/docs/v43/heatmap_histogram.png) + +## Histogram Aggregation Support for Elasticsearch + +Elasticsearch is the only supported data source that can return pre-bucketed data (data that is already grouped into ranges). With other data sources there is a risk of returning inaccurate data in a histogram due to using already aggregated data rather than raw data. This release adds support for Elasticsearch pre-bucketed data that can be visualized with the new [Heatmap Panel](http://docs.grafana.org/features/panels/heatmap/). + +## Heatmap Panel + +The Histogram support in the Graph Panel does not show changes over time - it aggregates all the data together for the chosen time range. To visualize a histogram over time, we have built a new [Heatmap Panel](http://docs.grafana.org/features/panels/heatmap/). + +Every column in a Heatmap is a histogram snapshot. Instead of visualizing higher values with higher bars, a heatmap visualizes higher values with color. The histogram shown above is equivalent to one column in the heatmap shown below. + +![](/img/docs/v43/heatmap_histogram_over_time.png) + +The Heatmap panel also works with Elasticsearch Histogram Aggregations for more accurate server side bucketing. + +![](/assets/img/blog/v4/elastic_heatmap.jpg) + +## MySQL Data Source (alpha) + +This release includes a [new core data source for MySQL](http://docs.grafana.org/features/datasources/mysql/). You can write any possible MySQL query and format it as either Time Series or Table Data allowing it be used with the Graph Panel, Table Panel and SingleStat Panel. + +We are still working on the MySQL data source. As it's missing some important features, like templating and macros and future changes could be breaking, we are +labeling the state of the data source as Alpha. Instead of holding up the release of v4.3 we are including it in its current shape to get some early feedback. So please try it out and let us know what you think on [twitter](https://twitter.com/intent/tweet?text=.%40grafana&source=4_3_beta_blog&related=blog) or on our [community forum](https://community.grafana.com/c/releases). Is this a feature that you would use? How can we make it better? + +**The query editor can show the generated and interpolated SQL that is sent to the MySQL server.** + +![](/img/docs/v43/mysql_table_query.png) + +**The query editor will also show any errors that resulted from running the query (very useful when you have a syntax error!).** + +![](/img/docs/v43/mysql_query_error.png) + +## Health Check Endpoint + +Now you can monitor the monitoring with the Health Check Endpoint! The new `/api/health` endpoint returns HTTP 200 OK if everything is up and HTTP 503 Error if the Grafana database cannot be pinged. + +## Lazy Load Panels + +Grafana now delays loading panels until they become visible (scrolled into view). This means panels out of view are not sending requests thereby reducing the load on your time series database. + +## Prometheus - Table Data (column per label) + +The Prometheus data source now supports the Table Data format by automatically assigning a column to a label. This makes it really easy to browse data in the table panel. + +![](/img/docs/v43/prom_table_cols_as_labels.png) + +## Other Highlights From The Changelog + +Changes: + +- **Table**: Support to change column header text [#3551](https://github.com/grafana/grafana/issues/3551) +- **InfluxDB**: influxdb query builder support for ORDER BY and LIMIT (allows TOPN queries) [#6065](https://github.com/grafana/grafana/issues/6065) Support influxdb's SLIMIT Feature [#7232](https://github.com/grafana/grafana/issues/7232) thx [@thuck](https://github.com/thuck) +- **Graph**: Support auto grid min/max when using log scale [#3090](https://github.com/grafana/grafana/issues/3090), thx [@bigbenhur](https://github.com/bigbenhur) +- **Prometheus**: Make Prometheus query field a textarea [#7663](https://github.com/grafana/grafana/issues/7663), thx [@hagen1778](https://github.com/hagen1778) +- **Server**: Support listening on a UNIX socket [#4030](https://github.com/grafana/grafana/issues/4030), thx [@mitjaziv](https://github.com/mitjaziv) + +Fixes: + +- **MySQL**: 4-byte UTF8 not supported when using MySQL database (allows Emojis in Dashboard Names) [#7958](https://github.com/grafana/grafana/issues/7958) +- **Dashboard**: Description tooltip is not fully displayed [#7970](https://github.com/grafana/grafana/issues/7970) + +Lots more enhancements and fixes can be found in the [Changelog](https://github.com/grafana/grafana/blob/master/CHANGELOG.md). + +## Download + +Head to the [v4.3 download page](https://grafana.com/grafana/download) for download links & instructions. + +## Thanks + +A big thanks to all the Grafana users who contribute by submitting PRs, bug reports, helping out on our [community site](https://community.grafana.com/) and providing feedback! + diff --git a/docs/sources/installation/debian.md b/docs/sources/installation/debian.md index 7bbf8c2052f..e9cb55ed36b 100644 --- a/docs/sources/installation/debian.md +++ b/docs/sources/installation/debian.md @@ -16,6 +16,7 @@ weight = 1 Description | Download ------------ | ------------- Stable for Debian-based Linux | [4.2.0 (x86-64 deb)](https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana_4.2.0_amd64.deb) +Beta for Debian-based Linux | [4.3.0-beta1 (x86-64 deb)](https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana_4.3.0-beta1_amd64.deb) Read [Upgrading Grafana]({{< relref "installation/upgrading.md" >}}) for tips and guidance on updating an existing installation. @@ -28,6 +29,14 @@ sudo apt-get install -y adduser libfontconfig sudo dpkg -i grafana_4.2.0_amd64.deb ``` +## Install Beta + +```bash +wget https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana_4.3.0-beta1_amd64.deb +sudo apt-get install -y adduser libfontconfig +sudo dpkg -i grafana_4.3.0-beta1_amd64.deb +``` + ## APT Repository Add the following line to your `/etc/apt/sources.list` file. diff --git a/docs/sources/installation/rpm.md b/docs/sources/installation/rpm.md index 69d676d6db8..a0fbd35610a 100644 --- a/docs/sources/installation/rpm.md +++ b/docs/sources/installation/rpm.md @@ -16,6 +16,7 @@ weight = 2 Description | Download ------------ | ------------- Stable for CentOS / Fedora / OpenSuse / Redhat Linux | [4.2.0 (x86-64 rpm)](https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana-4.2.0-1.x86_64.rpm) +Beta for CentOS / Fedora / OpenSuse / Redhat Linux | [4.3.0-beta1 (x86-64 rpm)](https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana-4.3.0-beta1.x86_64.rpm) Read [Upgrading Grafana]({{< relref "installation/upgrading.md" >}}) for tips and guidance on updating an existing installation. diff --git a/docs/sources/installation/windows.md b/docs/sources/installation/windows.md index d1327d80d3e..9f020d8f61c 100644 --- a/docs/sources/installation/windows.md +++ b/docs/sources/installation/windows.md @@ -14,6 +14,7 @@ weight = 3 Description | Download ------------ | ------------- Latest stable package for Windows | [grafana.4.2.0.windows-x64.zip](https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana-4.2.0.windows-x64.zip) +Beta package for Windows | [grafana-4.3.0-beta1.windows-x64.zip](https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana-4.3.0-beta1.windows-x64.zip) Read [Upgrading Grafana]({{< relref "installation/upgrading.md" >}}) for tips and guidance on updating an existing installation. From fcbe3066286f90b27e66a9d5bd5af75a7f3e4a4a Mon Sep 17 00:00:00 2001 From: Daniel Lee Date: Fri, 12 May 2017 13:36:21 +0200 Subject: [PATCH 79/85] release: version bump for package cloud script --- packaging/publish/publish_testing.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packaging/publish/publish_testing.sh b/packaging/publish/publish_testing.sh index 76419eb7a7e..8d27a35b826 100755 --- a/packaging/publish/publish_testing.sh +++ b/packaging/publish/publish_testing.sh @@ -1,6 +1,6 @@ #! /usr/bin/env bash -deb_ver=4.2.0-beta1 -rpm_ver=4.2.0-beta1 +deb_ver=4.3.0-beta1 +rpm_ver=4.3.0-beta1 wget https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana_${deb_ver}_amd64.deb From fd5fc0b7abc2058e61d7214634a1a4eaa1bc7ee8 Mon Sep 17 00:00:00 2001 From: Daniel Lee Date: Fri, 12 May 2017 14:19:46 +0200 Subject: [PATCH 80/85] docs: metadata change for guide post --- docs/sources/guides/whats-new-in-v4-3.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/sources/guides/whats-new-in-v4-3.md b/docs/sources/guides/whats-new-in-v4-3.md index ef37c56bfa0..3290f3cd990 100644 --- a/docs/sources/guides/whats-new-in-v4-3.md +++ b/docs/sources/guides/whats-new-in-v4-3.md @@ -7,7 +7,7 @@ type = "docs" name = "Version 4.3" identifier = "v4.3" parent = "whatsnew" -weight = -1 +weight = -2 +++ ## What's New in Grafana v4.3 From 9566197620503742085606a2a3bad42b8fc2817d Mon Sep 17 00:00:00 2001 From: Konstantin Koniev Date: Fri, 12 May 2017 15:59:56 +0300 Subject: [PATCH 81/85] Internationalise keybindings. (#8311) --- public/app/core/services/keybindingSrv.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/app/core/services/keybindingSrv.ts b/public/app/core/services/keybindingSrv.ts index acf0123962b..95ad66b568a 100644 --- a/public/app/core/services/keybindingSrv.ts +++ b/public/app/core/services/keybindingSrv.ts @@ -74,7 +74,7 @@ export class KeybindingSrv { evt.stopPropagation(); evt.returnValue = false; return this.$rootScope.$apply(fn.bind(this)); - }); + }, 'keydown'); } showDashEditView(view) { From f697f81950d50d78ac9adbc7b6bb00a327da82cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Mon, 15 May 2017 12:19:19 +0200 Subject: [PATCH 82/85] sqlite: fixed database table looked handling, now retries up to 5 times, fixes #7992 --- pkg/services/sqlstore/shared.go | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/pkg/services/sqlstore/shared.go b/pkg/services/sqlstore/shared.go index be4266477c7..72566a93583 100644 --- a/pkg/services/sqlstore/shared.go +++ b/pkg/services/sqlstore/shared.go @@ -1,9 +1,12 @@ package sqlstore import ( + "time" + "github.com/go-xorm/xorm" "github.com/grafana/grafana/pkg/bus" "github.com/grafana/grafana/pkg/log" + sqlite3 "github.com/mattn/go-sqlite3" ) type dbTransactionFunc func(sess *xorm.Session) error @@ -19,6 +22,10 @@ func (sess *session) publishAfterCommit(msg interface{}) { } func inTransaction(callback dbTransactionFunc) error { + return inTransactionWithRetry(callback, 0) +} + +func inTransactionWithRetry(callback dbTransactionFunc, retry int) error { var err error sess := x.NewSession() @@ -30,6 +37,16 @@ func inTransaction(callback dbTransactionFunc) error { err = callback(sess) + // special handling of database locked errors for sqlite, then we can retry 3 times + if sqlError, ok := err.(sqlite3.Error); ok && retry < 5 { + if sqlError.Code == sqlite3.ErrLocked { + sess.Rollback() + time.Sleep(time.Millisecond * time.Duration(10)) + sqlog.Info("Database table locked, sleeping then retrying", "retry", retry) + return inTransactionWithRetry(callback, retry+1) + } + } + if err != nil { sess.Rollback() return err From 8b11712f5e00988171312600344c62b42f5ab417 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Tue, 16 May 2017 14:29:30 +0200 Subject: [PATCH 83/85] fix: Graphite issue with toggle edit mode in query editor, fixes #8377 --- CHANGELOG.md | 8 +++++++- public/app/plugins/datasource/graphite/query_ctrl.ts | 1 + 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f80ebcc1381..88c5e9b08f5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +# 4.3.0-stable (unreleased) + +## Bug fixes + +* **Graphite**: Fixed issue with Toggle edit mode did in query editor [#8377](https://github.com/grafana/grafana/issues/8377) + # 4.3.0-beta1 (2017-05-12) ## Enhancements @@ -34,7 +40,7 @@ * **Server**: Support listening on a UNIX socket [#4030](https://github.com/grafana/grafana/issues/4030), thx [@mitjaziv](https://github.com/mitjaziv) * **Graph**: Support log scaling for values smaller 1 [#5278](https://github.com/grafana/grafana/issues/5278) * **InfluxDB**: Slow 'select measurement' rendering for InfluxDB [#2524](https://github.com/grafana/grafana/issues/2524), thx [@sbhenderson](https://github.com/sbhenderson) -* **Config**: Configurable signout menu activation [#7968](https://github.com/grafana/grafana/pull/7968), thx [@seuf](https://github.com/seuf) +* **Config**: Configurable signout menu activation [#7968](https://github.com/grafana/grafana/pull/7968), thx [@seuf](https://github.com/seuf) ## Fixes * **Table Panel**: Fixed annotation display in table panel, [#8023](https://github.com/grafana/grafana/issues/8023) diff --git a/public/app/plugins/datasource/graphite/query_ctrl.ts b/public/app/plugins/datasource/graphite/query_ctrl.ts index 1cf4406c7b8..ae72d7ab1d0 100644 --- a/public/app/plugins/datasource/graphite/query_ctrl.ts +++ b/public/app/plugins/datasource/graphite/query_ctrl.ts @@ -28,6 +28,7 @@ export class GraphiteQueryCtrl extends QueryCtrl { } toggleEditorMode() { + this.target.textEditor = !this.target.textEditor; this.parseTarget(); } From e8b798914d9a15a1a5c792e94754abfec9dd621d Mon Sep 17 00:00:00 2001 From: Daniel Lee Date: Tue, 16 May 2017 16:07:16 +0200 Subject: [PATCH 84/85] mysql: adds mapping for int/long --- pkg/tsdb/mysql/mysql.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pkg/tsdb/mysql/mysql.go b/pkg/tsdb/mysql/mysql.go index 4ebe467459d..d4bd9dbacc1 100644 --- a/pkg/tsdb/mysql/mysql.go +++ b/pkg/tsdb/mysql/mysql.go @@ -194,6 +194,8 @@ func (e MysqlExecutor) getTypedRowData(types []*sql.ColumnType, rows *core.Rows) values[i] = new(string) case mysql.FieldTypeNameVarChar: values[i] = new(string) + case mysql.FieldTypeNameLong: + values[i] = new(int) case mysql.FieldTypeNameLongLong: values[i] = new(int64) case mysql.FieldTypeNameDouble: From 02c79a638986617c9f876b108799f03b75c486e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Tue, 16 May 2017 16:28:58 +0200 Subject: [PATCH 85/85] influxdb: improvements to influxdb query editor, SLIMIT, LIMIT and ORDER BY now added on demand by plus button --- .../influxdb/partials/query.editor.html | 79 ++++++++++--------- .../plugins/datasource/influxdb/query_ctrl.ts | 37 +++++++-- 2 files changed, 74 insertions(+), 42 deletions(-) diff --git a/public/app/plugins/datasource/influxdb/partials/query.editor.html b/public/app/plugins/datasource/influxdb/partials/query.editor.html index 31bc7e6d8ad..2d613c034b2 100644 --- a/public/app/plugins/datasource/influxdb/partials/query.editor.html +++ b/public/app/plugins/datasource/influxdb/partials/query.editor.html @@ -1,7 +1,7 @@
    - +
    @@ -72,44 +72,51 @@
    -
    -
    - - -
    - -
    -
    - -
    - - -
    - -
    - - -
    -
    +
    +
    + + + +
    +
    -
    -
    +
    -
    -
    - - -
    -
    - -
    - +
    +
    + + +
    +
    +
    -
    -
    -
    -
    -
    +
    + +
    +
    + + +
    +
    +
    +
    +
    + +
    +
    + + +
    +
    + +
    + +
    +
    +
    +
    +
    +
    diff --git a/public/app/plugins/datasource/influxdb/query_ctrl.ts b/public/app/plugins/datasource/influxdb/query_ctrl.ts index c676c222d94..2240183fa48 100644 --- a/public/app/plugins/datasource/influxdb/query_ctrl.ts +++ b/public/app/plugins/datasource/influxdb/query_ctrl.ts @@ -33,11 +33,6 @@ export class InfluxQueryCtrl extends QueryCtrl { {text: 'Time series', value: 'time_series'}, {text: 'Table', value: 'table'}, ]; - this.orderByTime = [ - {text: 'ASC', value: 'ASC'}, - {text: 'DESC', value: 'DESC'}, - ]; - this.policySegment = uiSegmentSrv.newSegment(this.target.policy); if (!this.target.measurement) { @@ -70,6 +65,10 @@ export class InfluxQueryCtrl extends QueryCtrl { this.removeTagFilterSegment = uiSegmentSrv.newSegment({fake: true, value: '-- remove tag filter --'}); } + removeOrderByTime() { + this.target.orderByTime = 'ASC'; + } + buildSelectMenu() { var categories = queryPart.getCategories(); this.selectMenu = _.reduce(categories, function(memo, cat, key) { @@ -92,6 +91,15 @@ export class InfluxQueryCtrl extends QueryCtrl { if (!this.queryModel.hasFill()) { options.push(this.uiSegmentSrv.newSegment({value: 'fill(null)'})); } + if (!this.target.limit) { + options.push(this.uiSegmentSrv.newSegment({value: 'LIMIT'})); + } + if (!this.target.slimit) { + options.push(this.uiSegmentSrv.newSegment({value: 'SLIMIT'})); + } + if (this.target.orderByTime === 'ASC') { + options.push(this.uiSegmentSrv.newSegment({value: 'ORDER BY time DESC'})); + } if (!this.queryModel.hasGroupByTime()) { options.push(this.uiSegmentSrv.newSegment({value: 'time($interval)'})); } @@ -103,7 +111,24 @@ export class InfluxQueryCtrl extends QueryCtrl { } groupByAction() { - this.queryModel.addGroupBy(this.groupBySegment.value); + switch (this.groupBySegment.value) { + case 'LIMIT': { + this.target.limit = 10; + break; + } + case 'SLIMIT': { + this.target.slimit = 10; + break; + } + case 'ORDER BY time DESC': { + this.target.orderByTime = 'DESC'; + break; + } + default: { + this.queryModel.addGroupBy(this.groupBySegment.value); + } + } + var plusButton = this.uiSegmentSrv.newPlusButton(); this.groupBySegment.value = plusButton.value; this.groupBySegment.html = plusButton.html;