Commit a3b6efdb by Carl Bergquist

Merge pull request #2883 from mtanda/prometheus_annotation_support

Prometheus annotation support
parents 3a1dff66 20283a46
......@@ -188,6 +188,57 @@ export function PrometheusDatasource(instanceSettings, $q, backendSrv, templateS
}
};
this.annotationQuery = function(options) {
var annotation = options.annotation;
var expr = annotation.expr || '';
var tagKeys = annotation.tagKeys || '';
var titleFormat = annotation.titleFormat || '';
var textFormat = annotation.textFormat || '';
if (!expr) { return $q.when([]); }
var interpolated;
try {
interpolated = templateSrv.replace(expr);
} catch (err) {
return $q.reject(err);
}
var query = {
expr: interpolated,
step: '60s'
};
var start = getPrometheusTime(options.range.from, false);
var end = getPrometheusTime(options.range.to, true);
return this.performTimeSeriesQuery(query, start, end).then(function(results) {
var eventList = [];
tagKeys = tagKeys.split(',');
_.each(results.data.data.result, function(series) {
var tags = _.chain(series.metric)
.filter(function(v, k) {
return _.contains(tagKeys, k);
}).value();
_.each(series.values, function(value) {
if (value[1] === '1') {
var event = {
annotation: annotation,
time: Math.floor(value[0]) * 1000,
title: renderTemplate(titleFormat, series.metric),
tags: tags,
text: renderTemplate(textFormat, series.metric)
};
eventList.push(event);
}
});
});
return eventList;
});
};
this.testDatasource = function() {
return this.metricFindQuery('metrics(.*)').then(function() {
return { status: 'success', message: 'Data source is working', title: 'Success' };
......@@ -240,22 +291,26 @@ export function PrometheusDatasource(instanceSettings, $q, backendSrv, templateS
return getOriginalMetricName(labelData);
}
return renderTemplate(options.legendFormat, labelData) || '{}';
}
function renderTemplate(format, data) {
var originalSettings = _.templateSettings;
_.templateSettings = {
interpolate: /\{\{(.+?)\}\}/g
};
var template = _.template(templateSrv.replace(options.legendFormat));
var metricName;
var template = _.template(templateSrv.replace(format));
var result;
try {
metricName = template(labelData);
result = template(data);
} catch (e) {
metricName = '{}';
result = null;
}
_.templateSettings = originalSettings;
return metricName;
return result;
}
function getOriginalMetricName(labelData) {
......
......@@ -5,8 +5,13 @@ class PrometheusConfigCtrl {
static templateUrl = 'public/app/plugins/datasource/prometheus/partials/config.html';
}
class PrometheusAnnotationsQueryCtrl {
static templateUrl = 'public/app/plugins/datasource/prometheus/partials/annotations.editor.html';
}
export {
PrometheusDatasource as Datasource,
PrometheusQueryCtrl as QueryCtrl,
PrometheusConfigCtrl as ConfigCtrl
PrometheusConfigCtrl as ConfigCtrl,
PrometheusAnnotationsQueryCtrl as AnnotationsQueryCtrl,
};
<div class="editor-row">
<div class="section">
<h5>Search expression</h5>
<div class="editor-option">
<input type="text" class="span6" ng-model='ctrl.annotation.expr' placeholder="ALERTS"></input>
</div>
</div>
</div>
<div class="editor-row">
<div class="section">
<h5>Field formats</h5>
<div class="editor-option">
<label class="small">Title</label>
<input type="text" class="input-small" ng-model='ctrl.annotation.titleFormat' placeholder="alertname"></input>
</div>
<div class="editor-option">
<label class="small">Tags</label>
<input type="text" class="input-small" ng-model='ctrl.annotation.tagKeys' placeholder="label1,label2"></input>
</div>
<div class="editor-option">
<label class="small">Text</label>
<input type="text" class="input-small" ng-model='ctrl.annotation.textFormat' placeholder="instance"></input>
</div>
</div>
</div>
......@@ -3,5 +3,6 @@
"name": "Prometheus",
"id": "prometheus",
"metrics": true
"metrics": true,
"annotations": true
}
......@@ -157,4 +157,45 @@ describe('PrometheusDatasource', function() {
expect(results.length).to.be(3);
});
});
describe('When performing annotationQuery', function() {
var results;
var urlExpected = 'proxied/api/v1/query_range?query=' +
encodeURIComponent('ALERTS{alertstate="firing"}') +
'&start=1443438675&end=1443460275&step=60s';
var options = {
annotation: {
expr: 'ALERTS{alertstate="firing"}',
tagKeys: 'job',
titleFormat: '{{alertname}}',
textFormat: '{{instance}}'
},
range: {
from: moment(1443438674760),
to: moment(1443460274760)
}
};
var response = {
status: "success",
data: {
resultType: "matrix",
result: [{
metric: {"__name__": "ALERTS", alertname: "InstanceDown", alertstate: "firing", instance: "testinstance", job: "testjob"},
values: [[1443454528, "1"]]
}]
}
};
beforeEach(function() {
ctx.$httpBackend.expect('GET', urlExpected).respond(response);
ctx.ds.annotationQuery(options).then(function(data) { results = data; });
ctx.$httpBackend.flush();
});
it('should return annotation list', function() {
ctx.$rootScope.$apply();
expect(results.length).to.be(1);
expect(results[0].tags).to.contain('testjob');
expect(results[0].title).to.be('InstanceDown');
expect(results[0].text).to.be('testinstance');
expect(results[0].time).to.be(1443454528 * 1000);
});
});
});
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment