Commit f8b05e0f by Mitsuhiro Tanda

add prometheus annotation query

parent 80e15dd7
...@@ -188,6 +188,58 @@ export function PrometheusDatasource(instanceSettings, $q, backendSrv, templateS ...@@ -188,6 +188,58 @@ 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() { this.testDatasource = function() {
return this.metricFindQuery('metrics(.*)').then(function() { return this.metricFindQuery('metrics(.*)').then(function() {
return { status: 'success', message: 'Data source is working', title: 'Success' }; return { status: 'success', message: 'Data source is working', title: 'Success' };
...@@ -240,22 +292,25 @@ export function PrometheusDatasource(instanceSettings, $q, backendSrv, templateS ...@@ -240,22 +292,25 @@ export function PrometheusDatasource(instanceSettings, $q, backendSrv, templateS
return getOriginalMetricName(labelData); return getOriginalMetricName(labelData);
} }
var originalSettings = _.templateSettings; return renderTemplate(options.legendFormat, labelData) || '{}';
}
function renderTemplate(format, data) {
_.templateSettings = { _.templateSettings = {
interpolate: /\{\{(.+?)\}\}/g interpolate: /\{\{(.+?)\}\}/g
}; };
var template = _.template(templateSrv.replace(options.legendFormat)); var template = _.template(templateSrv.replace(format));
var metricName; var result;
try { try {
metricName = template(labelData); result = template(data);
} catch (e) { } catch (e) {
metricName = '{}'; result = null;
} }
_.templateSettings = originalSettings; _.templateSettings = originalSettings;
return metricName; return result;
} }
function getOriginalMetricName(labelData) { function getOriginalMetricName(labelData) {
......
...@@ -5,8 +5,13 @@ class PrometheusConfigCtrl { ...@@ -5,8 +5,13 @@ class PrometheusConfigCtrl {
static templateUrl = 'public/app/plugins/datasource/prometheus/partials/config.html'; static templateUrl = 'public/app/plugins/datasource/prometheus/partials/config.html';
} }
class PrometheusAnnotationsQueryCtrl {
static templateUrl = 'public/app/plugins/datasource/prometheus/partials/annotations.editor.html';
}
export { export {
PrometheusDatasource as Datasource, PrometheusDatasource as Datasource,
PrometheusQueryCtrl as QueryCtrl, 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='currentAnnotation.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='currentAnnotation.titleFormat' placeholder="alertname"></input>
</div>
<div class="editor-option">
<label class="small">Tags</label>
<input type="text" class="input-small" ng-model='currentAnnotation.tagKeys' placeholder="label1,label2"></input>
</div>
<div class="editor-option">
<label class="small">Text</label>
<input type="text" class="input-small" ng-model='currentAnnotation.textFormat' placeholder="instance"></input>
</div>
</div>
</div>
...@@ -3,5 +3,6 @@ ...@@ -3,5 +3,6 @@
"name": "Prometheus", "name": "Prometheus",
"id": "prometheus", "id": "prometheus",
"metrics": true "metrics": true,
"annotations": true
} }
...@@ -157,4 +157,39 @@ describe('PrometheusDatasource', function() { ...@@ -157,4 +157,39 @@ describe('PrometheusDatasource', function() {
expect(results.length).to.be(3); 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 annotation = {
expr: 'ALERTS{alertstate="firing"}',
tagKeys: 'job',
titleFormat: '{{alertname}}',
textFormat: '{{instance}}'
};
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(annotation, {from: moment(1443438674760), to: moment(1443460274760)}).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