2017-08-10 02:33:17 -05:00
|
|
|
///<reference path="../../../headers/common.d.ts" />
|
|
|
|
|
|
|
|
import {PrometheusDatasource} from "./datasource";
|
2017-09-06 06:50:32 -05:00
|
|
|
import _ from 'lodash';
|
2017-08-10 02:33:17 -05:00
|
|
|
|
|
|
|
export class PromCompleter {
|
2017-09-11 08:09:05 -05:00
|
|
|
labelQueryCache: any;
|
2017-09-06 06:50:32 -05:00
|
|
|
labelNameCache: any;
|
2017-09-11 08:09:05 -05:00
|
|
|
labelValueCache: any;
|
2017-09-06 06:50:32 -05:00
|
|
|
|
2017-10-17 05:26:16 -05:00
|
|
|
identifierRegexps = [/[\[\]a-zA-Z0-9_:=]/];
|
2017-08-10 02:33:17 -05:00
|
|
|
|
|
|
|
constructor(private datasource: PrometheusDatasource) {
|
2017-09-11 08:09:05 -05:00
|
|
|
this.labelQueryCache = {};
|
2017-09-06 06:50:32 -05:00
|
|
|
this.labelNameCache = {};
|
2017-09-11 08:09:05 -05:00
|
|
|
this.labelValueCache = {};
|
2017-08-10 02:33:17 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
getCompletions(editor, session, pos, prefix, callback) {
|
2017-09-07 03:10:52 -05:00
|
|
|
let token = session.getTokenAt(pos.row, pos.column);
|
|
|
|
|
2017-09-11 08:09:05 -05:00
|
|
|
var metricName;
|
2017-09-07 03:10:52 -05:00
|
|
|
switch (token.type) {
|
2017-09-11 19:31:05 -05:00
|
|
|
case 'entity.name.tag':
|
2017-09-11 08:09:05 -05:00
|
|
|
metricName = this.findMetricName(session, pos.row, pos.column);
|
2017-09-06 06:50:32 -05:00
|
|
|
if (!metricName) {
|
2017-09-11 08:09:05 -05:00
|
|
|
callback(null, this.transformToCompletions(['__name__', 'instance', 'job'], 'label name'));
|
2017-09-06 06:50:32 -05:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (this.labelNameCache[metricName]) {
|
|
|
|
callback(null, this.labelNameCache[metricName]);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-10-07 15:05:31 -05:00
|
|
|
return this.getLabelNameAndValueForMetric(metricName).then(result => {
|
2017-09-06 06:50:32 -05:00
|
|
|
var labelNames = this.transformToCompletions(
|
2017-09-11 08:09:05 -05:00
|
|
|
_.uniq(_.flatten(result.map(r => {
|
2017-09-06 06:50:32 -05:00
|
|
|
return Object.keys(r.metric);
|
|
|
|
})))
|
2017-09-11 08:09:05 -05:00
|
|
|
, 'label name');
|
2017-09-06 06:50:32 -05:00
|
|
|
this.labelNameCache[metricName] = labelNames;
|
|
|
|
callback(null, labelNames);
|
|
|
|
});
|
2017-09-11 19:31:05 -05:00
|
|
|
case 'string.quoted':
|
2017-09-11 08:09:05 -05:00
|
|
|
metricName = this.findMetricName(session, pos.row, pos.column);
|
|
|
|
if (!metricName) {
|
|
|
|
callback(null, []);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-09-11 19:31:05 -05:00
|
|
|
var labelNameToken = this.findToken(session, pos.row, pos.column, 'entity.name.tag', null, 'paren.lparen');
|
2017-09-11 08:09:05 -05:00
|
|
|
if (!labelNameToken) {
|
|
|
|
callback(null, []);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
var labelName = labelNameToken.value;
|
|
|
|
|
|
|
|
if (this.labelValueCache[metricName] && this.labelValueCache[metricName][labelName]) {
|
|
|
|
callback(null, this.labelValueCache[metricName][labelName]);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-10-07 15:05:31 -05:00
|
|
|
return this.getLabelNameAndValueForMetric(metricName).then(result => {
|
2017-09-11 08:09:05 -05:00
|
|
|
var labelValues = this.transformToCompletions(
|
|
|
|
_.uniq(result.map(r => {
|
|
|
|
return r.metric[labelName];
|
|
|
|
}))
|
|
|
|
, 'label value');
|
|
|
|
this.labelValueCache[metricName] = this.labelValueCache[metricName] || {};
|
|
|
|
this.labelValueCache[metricName][labelName] = labelValues;
|
|
|
|
callback(null, labelValues);
|
|
|
|
});
|
2017-09-07 03:10:52 -05:00
|
|
|
}
|
|
|
|
|
2017-08-10 03:08:09 -05:00
|
|
|
if (prefix === '[') {
|
|
|
|
var vectors = [];
|
|
|
|
for (let unit of ['s', 'm', 'h']) {
|
|
|
|
for (let value of [1,5,10,30]) {
|
|
|
|
vectors.push({caption: value+unit, value: '['+value+unit, meta: 'range vector'});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
callback(null, vectors);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-08-10 02:33:17 -05:00
|
|
|
var query = prefix;
|
2017-08-10 03:08:09 -05:00
|
|
|
|
2017-09-07 03:09:59 -05:00
|
|
|
return this.datasource.performSuggestQuery(query, true).then(metricNames => {
|
2017-08-10 02:33:17 -05:00
|
|
|
callback(null, metricNames.map(name => {
|
2017-08-10 03:08:09 -05:00
|
|
|
let value = name;
|
|
|
|
if (prefix === '(') {
|
|
|
|
value = '(' + name;
|
|
|
|
}
|
|
|
|
|
2017-08-10 02:33:17 -05:00
|
|
|
return {
|
|
|
|
caption: name,
|
2017-08-10 03:08:09 -05:00
|
|
|
value: value,
|
2017-08-10 02:33:17 -05:00
|
|
|
meta: 'metric',
|
|
|
|
};
|
|
|
|
}));
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2017-09-11 08:09:05 -05:00
|
|
|
getLabelNameAndValueForMetric(metricName) {
|
|
|
|
if (this.labelQueryCache[metricName]) {
|
|
|
|
return Promise.resolve(this.labelQueryCache[metricName]);
|
|
|
|
}
|
|
|
|
var op = '=~';
|
|
|
|
if (/[a-zA-Z_:][a-zA-Z0-9_:]*/.test(metricName)) {
|
|
|
|
op = '=';
|
|
|
|
}
|
|
|
|
var expr = '{__name__' + op + '"' + metricName + '"}';
|
|
|
|
return this.datasource.performInstantQuery({ expr: expr }, new Date().getTime() / 1000).then(response => {
|
|
|
|
this.labelQueryCache[metricName] = response.data.data.result;
|
|
|
|
return response.data.data.result;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
transformToCompletions(words, meta) {
|
2017-09-06 06:50:32 -05:00
|
|
|
return words.map(name => {
|
|
|
|
return {
|
|
|
|
caption: name,
|
|
|
|
value: name,
|
2017-09-11 08:09:05 -05:00
|
|
|
meta: meta,
|
2017-09-06 06:50:32 -05:00
|
|
|
score: Number.MAX_VALUE
|
|
|
|
};
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
findMetricName(session, row, column) {
|
|
|
|
var metricName = '';
|
|
|
|
|
|
|
|
var tokens;
|
2017-09-11 19:31:05 -05:00
|
|
|
var nameLabelNameToken = this.findToken(session, row, column, 'entity.name.tag', '__name__', 'paren.lparen');
|
2017-09-06 06:50:32 -05:00
|
|
|
if (nameLabelNameToken) {
|
|
|
|
tokens = session.getTokens(nameLabelNameToken.row);
|
|
|
|
var nameLabelValueToken = tokens[nameLabelNameToken.index + 2];
|
2017-09-11 19:31:05 -05:00
|
|
|
if (nameLabelValueToken && nameLabelValueToken.type === 'string.quoted') {
|
2017-09-06 06:50:32 -05:00
|
|
|
metricName = nameLabelValueToken.value.slice(1, -1); // cut begin/end quotation
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
var metricNameToken = this.findToken(session, row, column, 'identifier', null, null);
|
|
|
|
if (metricNameToken) {
|
|
|
|
tokens = session.getTokens(metricNameToken.row);
|
|
|
|
if (tokens[metricNameToken.index + 1].type === 'paren.lparen') {
|
|
|
|
metricName = metricNameToken.value;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return metricName;
|
|
|
|
}
|
|
|
|
|
|
|
|
findToken(session, row, column, target, value, guard) {
|
|
|
|
var tokens, idx;
|
|
|
|
for (var r = row; r >= 0; r--) {
|
|
|
|
tokens = session.getTokens(r);
|
|
|
|
if (r === row) { // current row
|
|
|
|
var c = 0;
|
|
|
|
for (idx = 0; idx < tokens.length; idx++) {
|
|
|
|
c += tokens[idx].value.length;
|
|
|
|
if (c >= column) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
idx = tokens.length - 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (; idx >= 0; idx--) {
|
|
|
|
if (tokens[idx].type === guard) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (tokens[idx].type === target
|
|
|
|
&& (!value || tokens[idx].value === value)) {
|
|
|
|
tokens[idx].row = r;
|
|
|
|
tokens[idx].index = idx;
|
|
|
|
return tokens[idx];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2017-08-10 02:33:17 -05:00
|
|
|
}
|