From 665caa8e08735fcc788492a333ac8db1210f7d50 Mon Sep 17 00:00:00 2001 From: Ricky Moorhouse Date: Fri, 26 May 2017 12:30:56 +0100 Subject: [PATCH 001/282] Include triggering metrics to pagerduty alerts Assist the person receiving the alert in identifying the cause Based on the slack notifier fields this will include upto 4 triggering metrics in the custom details section in the pagerduty incident Fixes #8479 --- pkg/services/alerting/notifiers/pagerduty.go | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/pkg/services/alerting/notifiers/pagerduty.go b/pkg/services/alerting/notifiers/pagerduty.go index 0c98ab00e20..7b3a06c0c6b 100644 --- a/pkg/services/alerting/notifiers/pagerduty.go +++ b/pkg/services/alerting/notifiers/pagerduty.go @@ -74,6 +74,16 @@ func (this *PagerdutyNotifier) Notify(evalContext *alerting.EvalContext) error { if evalContext.Rule.State == m.AlertStateOK { eventType = "resolve" } + customData := make([]map[string]interface{}, 0) + fieldLimitCount := 4 + for index, evt := range evalContext.EvalMatches { + customData = append(customData, map[string]interface{}{ + evt.Metric: evt.Value, + }) + if index > fieldLimitCount { + break + } + } this.log.Info("Notifying Pagerduty", "event_type", eventType) @@ -81,6 +91,7 @@ func (this *PagerdutyNotifier) Notify(evalContext *alerting.EvalContext) error { bodyJSON.Set("service_key", this.Key) bodyJSON.Set("description", evalContext.Rule.Name+" - "+evalContext.Rule.Message) bodyJSON.Set("client", "Grafana") + bodyJSON.Set("details", customData) bodyJSON.Set("event_type", eventType) bodyJSON.Set("incident_key", "alertId-"+strconv.FormatInt(evalContext.Rule.Id, 10)) From 3e0b92d6feca7da9603ca2038099fbdc6e6c40e6 Mon Sep 17 00:00:00 2001 From: Chris Burkhart Date: Mon, 24 Jul 2017 09:55:57 -0700 Subject: [PATCH 002/282] Enable datasources to be able to round off to a UTC day properly --- public/app/core/utils/datemath.ts | 8 ++++++-- public/app/features/dashboard/time_srv.ts | 5 +++-- public/app/features/panel/metrics_panel_ctrl.ts | 3 +++ public/test/core/utils/datemath_specs.ts | 8 ++++++++ 4 files changed, 20 insertions(+), 4 deletions(-) diff --git a/public/app/core/utils/datemath.ts b/public/app/core/utils/datemath.ts index 2aa793016ba..f5608443f49 100644 --- a/public/app/core/utils/datemath.ts +++ b/public/app/core/utils/datemath.ts @@ -5,7 +5,7 @@ import moment from 'moment'; var units = ['y', 'M', 'w', 'd', 'h', 'm', 's']; -export function parse(text, roundUp?) { +export function parse(text, roundUp?, timezone?) { if (!text) { return undefined; } if (moment.isMoment(text)) { return text; } if (_.isDate(text)) { return moment(text); } @@ -16,7 +16,11 @@ export function parse(text, roundUp?) { var parseString; if (text.substring(0, 3) === 'now') { - time = moment(); + if (timezone === 'utc') { + time = moment.utc(); + } else { + time = moment(); + } mathString = text.substring('now'.length); } else { index = text.indexOf('||'); diff --git a/public/app/features/dashboard/time_srv.ts b/public/app/features/dashboard/time_srv.ts index abde4152b63..ef5dead40a7 100644 --- a/public/app/features/dashboard/time_srv.ts +++ b/public/app/features/dashboard/time_srv.ts @@ -199,10 +199,11 @@ class TimeSrv { from: moment.isMoment(this.time.from) ? moment(this.time.from) : this.time.from, to: moment.isMoment(this.time.to) ? moment(this.time.to) : this.time.to, }; + var timezone = this.dashboard && this.dashboard.getTimezone ? this.dashboard.getTimezone() : 'local'; return { - from: dateMath.parse(raw.from, false), - to: dateMath.parse(raw.to, true), + from: dateMath.parse(raw.from, false, timezone), + to: dateMath.parse(raw.to, true, timezone), raw: raw }; } diff --git a/public/app/features/panel/metrics_panel_ctrl.ts b/public/app/features/panel/metrics_panel_ctrl.ts index 0e94df437a8..1ae24066bb9 100644 --- a/public/app/features/panel/metrics_panel_ctrl.ts +++ b/public/app/features/panel/metrics_panel_ctrl.ts @@ -221,7 +221,10 @@ class MetricsPanelCtrl extends PanelCtrl { "__interval_ms": {text: this.intervalMs, value: this.intervalMs}, }); + var timezone = this.dashboard.getTimezone ? this.dashboard.getTimezone() : 'local'; + var metricsQuery = { + timezone: timezone, panelId: this.panel.id, range: this.range, rangeRaw: this.range.raw, diff --git a/public/test/core/utils/datemath_specs.ts b/public/test/core/utils/datemath_specs.ts index c6096485f65..b919653c2c8 100644 --- a/public/test/core/utils/datemath_specs.ts +++ b/public/test/core/utils/datemath_specs.ts @@ -46,6 +46,14 @@ describe("DateMath", () => { expect(startOfDay).to.be(expected.getTime()); }); + it("now/d on a utc dashboard should be start of the current day in UTC time", () => { + var today = new Date(); + var expected = new Date(Date.UTC(today.getFullYear(), today.getMonth(), today.getDate(), 0, 0, 0, 0)); + + var startOfDay = dateMath.parse('now/d', false, 'utc').valueOf(); + expect(startOfDay).to.be(expected.getTime()); + }); + describe('subtraction', () => { var now; var anchored; From c6f7d34c5528d1638ba5628b7c94373b4386c283 Mon Sep 17 00:00:00 2001 From: Joseph Weigl Date: Thu, 24 Aug 2017 13:40:19 +0200 Subject: [PATCH 003/282] Reorder editorconfig --- .editorconfig | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.editorconfig b/.editorconfig index 386c27fceb8..146224e7330 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,13 +1,6 @@ # http://editorconfig.org root = true -[*.go] -indent_style = tab -indent_size = 4 -charset = utf-8 -trim_trailing_whitespace = true -insert_final_newline = true - [*] indent_style = space indent_size = 2 @@ -15,5 +8,12 @@ charset = utf-8 trim_trailing_whitespace = true insert_final_newline = true +[*.go] +indent_style = tab +indent_size = 4 +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + [*.md] trim_trailing_whitespace = false From 9666f45e9c2447db0614f902eb08b3f82673c904 Mon Sep 17 00:00:00 2001 From: Joseph Weigl Date: Thu, 24 Aug 2017 13:40:33 +0200 Subject: [PATCH 004/282] Add values to the hipchat card --- pkg/services/alerting/notifiers/hipchat.go | 31 +++++++++++----------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/pkg/services/alerting/notifiers/hipchat.go b/pkg/services/alerting/notifiers/hipchat.go index 0eb21865b9f..03176c64729 100644 --- a/pkg/services/alerting/notifiers/hipchat.go +++ b/pkg/services/alerting/notifiers/hipchat.go @@ -84,15 +84,13 @@ func (this *HipChatNotifier) Notify(evalContext *alerting.EvalContext) error { return err } - message := evalContext.GetNotificationTitle() + " in state " + evalContext.GetStateModel().Text + "
Check Dasboard" - fields := make([]map[string]interface{}, 0) - message += "
" + attributes := make([]map[string]interface{}, 0) for index, evt := range evalContext.EvalMatches { - message += evt.Metric + " :: " + strconv.FormatFloat(evt.Value.Float64, 'f', -1, 64) + "
" - fields = append(fields, map[string]interface{}{ - "title": evt.Metric, - "value": evt.Value, - "short": true, + attributes = append(attributes, map[string]interface{}{ + "label": evt.Metric, + "value": map[string]interface{}{ + "label": strconv.FormatFloat(evt.Value.Float64, 'f', -1, 64), + }, }) if index > maxFieldCount { break @@ -100,13 +98,15 @@ func (this *HipChatNotifier) Notify(evalContext *alerting.EvalContext) error { } if evalContext.Error != nil { - fields = append(fields, map[string]interface{}{ - "title": "Error message", - "value": evalContext.Error.Error(), - "short": false, + attributes = append(attributes, map[string]interface{}{ + "label": "Error message", + "value": map[string]interface{}{ + "label": evalContext.Error.Error(), + }, }) } + message := "" if evalContext.Rule.State != models.AlertStateOK { //dont add message when going back to alert state ok. message += " " + evalContext.Rule.Message } @@ -123,15 +123,16 @@ func (this *HipChatNotifier) Notify(evalContext *alerting.EvalContext) error { // Add a card with link to the dashboard card := map[string]interface{}{ - "style": "link", + "style": "application", "url": ruleUrl, "id": "1", "title": evalContext.GetNotificationTitle(), - "description": evalContext.GetNotificationTitle() + " in state " + evalContext.GetStateModel().Text, + "description": message, "icon": map[string]interface{}{ "url": "https://grafana.com/assets/img/fav32.png", }, - "date": evalContext.EndTime.Unix(), + "date": evalContext.EndTime.Unix(), + "attributes": attributes, } body := map[string]interface{}{ From 81d3ab37c372642ebf151d05378663d9402974f3 Mon Sep 17 00:00:00 2001 From: Joseph Weigl Date: Thu, 24 Aug 2017 14:52:23 +0200 Subject: [PATCH 005/282] Add thumbnail to card --- pkg/services/alerting/notifiers/hipchat.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/pkg/services/alerting/notifiers/hipchat.go b/pkg/services/alerting/notifiers/hipchat.go index 03176c64729..757120db25b 100644 --- a/pkg/services/alerting/notifiers/hipchat.go +++ b/pkg/services/alerting/notifiers/hipchat.go @@ -134,6 +134,14 @@ func (this *HipChatNotifier) Notify(evalContext *alerting.EvalContext) error { "date": evalContext.EndTime.Unix(), "attributes": attributes, } + if len(evalContext.ImagePublicUrl) > 0 { + card["thumbnail"] = map[string]interface{}{ + "url": evalContext.ImagePublicUrl, + "url@2x": evalContext.ImagePublicUrl, + "width": 1193, + "height": 564, + } + } body := map[string]interface{}{ "message": message, From 84c6caabc5b06a8fdb342714e3fed2cd5adc6774 Mon Sep 17 00:00:00 2001 From: Alin Sinpalean Date: Mon, 28 Aug 2017 15:45:51 +0200 Subject: [PATCH 006/282] Prometheus: Fix actual step computation logic when a min_step is specified and the range is longer than min_step * 11000. --- public/app/plugins/datasource/prometheus/datasource.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/app/plugins/datasource/prometheus/datasource.ts b/public/app/plugins/datasource/prometheus/datasource.ts index 36fc1e5de4b..9e416de44c7 100644 --- a/public/app/plugins/datasource/prometheus/datasource.ts +++ b/public/app/plugins/datasource/prometheus/datasource.ts @@ -128,7 +128,7 @@ export function PrometheusDatasource(instanceSettings, $q, backendSrv, templateS // Prometheus drop query if range/step > 11000 // calibrate step if it is too big if (step !== 0 && range / step > 11000) { - return Math.ceil(range / 11000); + step = Math.ceil(range / 11000); } return Math.max(step, autoStep); }; From 56cb16ff5bd6273128593657b10d3a9b9ed6abe6 Mon Sep 17 00:00:00 2001 From: Mitsuhiro Tanda Date: Thu, 7 Sep 2017 01:03:02 +0900 Subject: [PATCH 007/282] (prometheus) instant query support --- .../datasource/prometheus/datasource.ts | 25 +++++++++++- .../datasource/prometheus/query_ctrl.ts | 1 + .../prometheus/specs/datasource_specs.ts | 39 +++++++++++++++++-- 3 files changed, 60 insertions(+), 5 deletions(-) diff --git a/public/app/plugins/datasource/prometheus/datasource.ts b/public/app/plugins/datasource/prometheus/datasource.ts index 4b4bfb4cdde..3544a702f29 100644 --- a/public/app/plugins/datasource/prometheus/datasource.ts +++ b/public/app/plugins/datasource/prometheus/datasource.ts @@ -100,6 +100,7 @@ export class PrometheusDatasource { var query: any = {}; query.expr = this.templateSrv.replace(target.expr, options.scopedVars, self.interpolateQueryExpr); query.requestId = options.panelId + target.refId; + query.format = target.format; var interval = this.templateSrv.replace(target.interval, options.scopedVars) || options.interval; var intervalFactor = target.intervalFactor || 1; @@ -115,7 +116,11 @@ export class PrometheusDatasource { } var allQueryPromise = _.map(queries, query => { - return this.performTimeSeriesQuery(query, start, end); + if (query.format === 'time_series') { + return this.performTimeSeriesQuery(query, start, end); + } else { + return this.performInstantQuery(query, end); + } }); return this.$q.all(allQueryPromise).then(responseList => { @@ -131,7 +136,11 @@ export class PrometheusDatasource { result.push(self.transformMetricDataToTable(response.data.data.result)); } else { for (let metricData of response.data.data.result) { - result.push(self.transformMetricData(metricData, activeTargets[index], start, end)); + if (response.data.data.resultType === 'matrix') { + result.push(self.transformMetricData(metricData, activeTargets[index], start, end)); + } else if (response.data.data.resultType === 'vector') { + result.push(self.transformInstantMetricData(metricData, activeTargets[index])); + } } } }); @@ -158,6 +167,11 @@ export class PrometheusDatasource { return this._request('GET', url, query.requestId); } + performInstantQuery(query, time) { + var url = '/api/v1/query?query=' + encodeURIComponent(query.expr) + '&time=' + time; + return this._request('GET', url, query.requestId); + } + performSuggestQuery(query, cache = false) { var url = '/api/v1/label/__name__/values'; @@ -342,6 +356,13 @@ export class PrometheusDatasource { return table; } + transformInstantMetricData(md, options) { + var dps = [], metricLabel = null; + metricLabel = this.createMetricLabel(md.metric, options); + dps.push([parseFloat(md.value[1]), md.value[0] * 1000]); + return { target: metricLabel, datapoints: dps }; + } + createMetricLabel(labelData, options) { if (_.isUndefined(options) || _.isEmpty(options.legendFormat)) { return this.getOriginalMetricName(labelData); diff --git a/public/app/plugins/datasource/prometheus/query_ctrl.ts b/public/app/plugins/datasource/prometheus/query_ctrl.ts index 17a564d0b46..9a3a9603f61 100644 --- a/public/app/plugins/datasource/prometheus/query_ctrl.ts +++ b/public/app/plugins/datasource/prometheus/query_ctrl.ts @@ -36,6 +36,7 @@ class PrometheusQueryCtrl extends QueryCtrl { this.formats = [ {text: 'Time series', value: 'time_series'}, {text: 'Table', value: 'table'}, + {text: 'Instant', value: 'instant'}, ]; this.updateLink(); diff --git a/public/app/plugins/datasource/prometheus/specs/datasource_specs.ts b/public/app/plugins/datasource/prometheus/specs/datasource_specs.ts index 9588090c7bb..95ccbbb6325 100644 --- a/public/app/plugins/datasource/prometheus/specs/datasource_specs.ts +++ b/public/app/plugins/datasource/prometheus/specs/datasource_specs.ts @@ -26,7 +26,7 @@ describe('PrometheusDatasource', function() { '&start=1443438675&end=1443460275&step=60'; var query = { range: { from: moment(1443438674760), to: moment(1443460274760) }, - targets: [{ expr: 'test{job="testjob"}' }], + targets: [{ expr: 'test{job="testjob"}', format: 'time_series' }], interval: '60s' }; var response = { @@ -62,7 +62,7 @@ describe('PrometheusDatasource', function() { '&start=' + start + '&end=' + end + '&step=' + step; var query = { range: { from: moment(1443438674760), to: moment(1443460274760) }, - targets: [{ expr: 'test{job="testjob"}' }], + targets: [{ expr: 'test{job="testjob"}', format: 'time_series' }], interval: '60s' }; var response = { @@ -119,7 +119,40 @@ describe('PrometheusDatasource', function() { expect(results.data[1].datapoints[3][0]).to.be(null); }); }); - describe('When performing annotationQuery', function() { + describe('When querying prometheus with one target and format = instant', function () { + var results; + var urlExpected = 'proxied/api/v1/query?query=' + + encodeURIComponent('test{job="testjob"}') + + '&time=1443460275'; + var query = { + range: { from: moment(1443438674760), to: moment(1443460274760) }, + targets: [{ expr: 'test{job="testjob"}', format: 'instant' }], + interval: '60s' + }; + var response = { + status: "success", + data: { + resultType: "vector", + result: [{ + metric: { "__name__": "test", job: "testjob" }, + value: [1443454528, "3846"] + }] + } + }; + beforeEach(function () { + ctx.$httpBackend.expect('GET', urlExpected).respond(response); + ctx.ds.query(query).then(function (data) { results = data; }); + ctx.$httpBackend.flush(); + }); + it('should generate the correct query', function () { + ctx.$httpBackend.verifyNoOutstandingExpectation(); + }); + it('should return series list', function () { + expect(results.data.length).to.be(1); + expect(results.data[0].target).to.be('test{job="testjob"}'); + }); + }); + describe('When performing annotationQuery', function () { var results; var urlExpected = 'proxied/api/v1/query_range?query=' + encodeURIComponent('ALERTS{alertstate="firing"}') + From 6f8110956df68c31f7ffaf8bbdbaa87d7a6c78f1 Mon Sep 17 00:00:00 2001 From: Mitsuhiro Tanda Date: Thu, 7 Sep 2017 18:34:46 +0900 Subject: [PATCH 008/282] (prometheus) support instant query for table format, use checkbox to switch query type --- .../datasource/prometheus/datasource.ts | 7 ++- .../prometheus/partials/query.editor.html | 2 + .../datasource/prometheus/query_ctrl.ts | 4 +- .../prometheus/specs/datasource_specs.ts | 45 ++++++++++++++++++- 4 files changed, 53 insertions(+), 5 deletions(-) diff --git a/public/app/plugins/datasource/prometheus/datasource.ts b/public/app/plugins/datasource/prometheus/datasource.ts index 3544a702f29..0975870f842 100644 --- a/public/app/plugins/datasource/prometheus/datasource.ts +++ b/public/app/plugins/datasource/prometheus/datasource.ts @@ -100,7 +100,7 @@ export class PrometheusDatasource { var query: any = {}; query.expr = this.templateSrv.replace(target.expr, options.scopedVars, self.interpolateQueryExpr); query.requestId = options.panelId + target.refId; - query.format = target.format; + query.instant = target.instant; var interval = this.templateSrv.replace(target.interval, options.scopedVars) || options.interval; var intervalFactor = target.intervalFactor || 1; @@ -116,7 +116,7 @@ export class PrometheusDatasource { } var allQueryPromise = _.map(queries, query => { - if (query.format === 'time_series') { + if (!query.instant) { return this.performTimeSeriesQuery(query, start, end); } else { return this.performInstantQuery(query, end); @@ -333,6 +333,9 @@ export class PrometheusDatasource { // Populate rows, set value to empty string when label not present. _.each(md, function(series) { + if (series.value) { + series.values = [series.value]; + } if (series.values) { for (i = 0; i < series.values.length; i++) { var values = series.values[i]; diff --git a/public/app/plugins/datasource/prometheus/partials/query.editor.html b/public/app/plugins/datasource/prometheus/partials/query.editor.html index 5140c9e7bbd..2099c4348c2 100644 --- a/public/app/plugins/datasource/prometheus/partials/query.editor.html +++ b/public/app/plugins/datasource/prometheus/partials/query.editor.html @@ -45,6 +45,8 @@
+ +