| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405 |
- import { PrometheusDatasource } from './datasource';
- import _ from 'lodash';
- export class PromCompleter {
- labelQueryCache: any;
- labelNameCache: any;
- labelValueCache: any;
- templateVariableCompletions: any;
- identifierRegexps = [/\[/, /[a-zA-Z0-9_:]/];
- constructor(private datasource: PrometheusDatasource, private templateSrv) {
- this.labelQueryCache = {};
- this.labelNameCache = {};
- this.labelValueCache = {};
- this.templateVariableCompletions = this.templateSrv.variables.map(variable => {
- return {
- caption: '$' + variable.name,
- value: '$' + variable.name,
- meta: 'variable',
- score: Number.MAX_VALUE,
- };
- });
- }
- getCompletions(editor, session, pos, prefix, callback) {
- const wrappedCallback = (err, completions) => {
- completions = completions.concat(this.templateVariableCompletions);
- return callback(err, completions);
- };
- const token = session.getTokenAt(pos.row, pos.column);
- switch (token.type) {
- case 'entity.name.tag.label-matcher':
- this.getCompletionsForLabelMatcherName(session, pos).then(completions => {
- wrappedCallback(null, completions);
- });
- return;
- case 'string.quoted.label-matcher':
- this.getCompletionsForLabelMatcherValue(session, pos).then(completions => {
- wrappedCallback(null, completions);
- });
- return;
- case 'entity.name.tag.label-list-matcher':
- this.getCompletionsForBinaryOperator(session, pos).then(completions => {
- wrappedCallback(null, completions);
- });
- return;
- }
- if (token.type === 'paren.lparen' && token.value === '[') {
- const vectors = [];
- for (const unit of ['s', 'm', 'h']) {
- for (const value of [1, 5, 10, 30]) {
- vectors.push({
- caption: value + unit,
- value: '[' + value + unit,
- meta: 'range vector',
- });
- }
- }
- vectors.unshift({
- caption: '$__interval_ms',
- value: '[$__interval_ms',
- meta: 'range vector',
- });
- vectors.unshift({
- caption: '$__interval',
- value: '[$__interval',
- meta: 'range vector',
- });
- wrappedCallback(null, vectors);
- return;
- }
- const query = prefix;
- return this.datasource.performSuggestQuery(query, true).then(metricNames => {
- wrappedCallback(
- null,
- metricNames.map(name => {
- let value = name;
- if (prefix === '(') {
- value = '(' + name;
- }
- return {
- caption: name,
- value: value,
- meta: 'metric',
- };
- })
- );
- });
- }
- getCompletionsForLabelMatcherName(session, pos) {
- const metricName = this.findMetricName(session, pos.row, pos.column);
- if (!metricName) {
- return Promise.resolve(this.transformToCompletions(['__name__', 'instance', 'job'], 'label name'));
- }
- if (this.labelNameCache[metricName]) {
- return Promise.resolve(this.labelNameCache[metricName]);
- }
- return this.getLabelNameAndValueForExpression(metricName, 'metricName').then(result => {
- const labelNames = this.transformToCompletions(
- _.uniq(
- _.flatten(
- result.map(r => {
- return Object.keys(r);
- })
- )
- ),
- 'label name'
- );
- this.labelNameCache[metricName] = labelNames;
- return Promise.resolve(labelNames);
- });
- }
- getCompletionsForLabelMatcherValue(session, pos) {
- const metricName = this.findMetricName(session, pos.row, pos.column);
- if (!metricName) {
- return Promise.resolve([]);
- }
- const labelNameToken = this.findToken(
- session,
- pos.row,
- pos.column,
- 'entity.name.tag.label-matcher',
- null,
- 'paren.lparen.label-matcher'
- );
- if (!labelNameToken) {
- return Promise.resolve([]);
- }
- const labelName = labelNameToken.value;
- if (this.labelValueCache[metricName] && this.labelValueCache[metricName][labelName]) {
- return Promise.resolve(this.labelValueCache[metricName][labelName]);
- }
- return this.getLabelNameAndValueForExpression(metricName, 'metricName').then(result => {
- const labelValues = this.transformToCompletions(
- _.uniq(
- result.map(r => {
- return r[labelName];
- })
- ),
- 'label value'
- );
- this.labelValueCache[metricName] = this.labelValueCache[metricName] || {};
- this.labelValueCache[metricName][labelName] = labelValues;
- return Promise.resolve(labelValues);
- });
- }
- getCompletionsForBinaryOperator(session, pos) {
- const keywordOperatorToken = this.findToken(session, pos.row, pos.column, 'keyword.control', null, 'identifier');
- if (!keywordOperatorToken) {
- return Promise.resolve([]);
- }
- let rparenToken, expr;
- switch (keywordOperatorToken.value) {
- case 'by':
- case 'without':
- rparenToken = this.findToken(
- session,
- keywordOperatorToken.row,
- keywordOperatorToken.column,
- 'paren.rparen',
- null,
- 'identifier'
- );
- if (!rparenToken) {
- return Promise.resolve([]);
- }
- expr = this.findExpressionMatchedParen(session, rparenToken.row, rparenToken.column);
- if (expr === '') {
- return Promise.resolve([]);
- }
- return this.getLabelNameAndValueForExpression(expr, 'expression').then(result => {
- const labelNames = this.transformToCompletions(
- _.uniq(
- _.flatten(
- result.map(r => {
- return Object.keys(r);
- })
- )
- ),
- 'label name'
- );
- this.labelNameCache[expr] = labelNames;
- return labelNames;
- });
- case 'on':
- case 'ignoring':
- case 'group_left':
- case 'group_right':
- const binaryOperatorToken = this.findToken(
- session,
- keywordOperatorToken.row,
- keywordOperatorToken.column,
- 'keyword.operator.binary',
- null,
- 'identifier'
- );
- if (!binaryOperatorToken) {
- return Promise.resolve([]);
- }
- rparenToken = this.findToken(
- session,
- binaryOperatorToken.row,
- binaryOperatorToken.column,
- 'paren.rparen',
- null,
- 'identifier'
- );
- if (rparenToken) {
- expr = this.findExpressionMatchedParen(session, rparenToken.row, rparenToken.column);
- if (expr === '') {
- return Promise.resolve([]);
- }
- return this.getLabelNameAndValueForExpression(expr, 'expression').then(result => {
- const labelNames = this.transformToCompletions(
- _.uniq(
- _.flatten(
- result.map(r => {
- return Object.keys(r);
- })
- )
- ),
- 'label name'
- );
- this.labelNameCache[expr] = labelNames;
- return labelNames;
- });
- } else {
- const metricName = this.findMetricName(session, binaryOperatorToken.row, binaryOperatorToken.column);
- return this.getLabelNameAndValueForExpression(metricName, 'metricName').then(result => {
- const labelNames = this.transformToCompletions(
- _.uniq(
- _.flatten(
- result.map(r => {
- return Object.keys(r);
- })
- )
- ),
- 'label name'
- );
- this.labelNameCache[metricName] = labelNames;
- return Promise.resolve(labelNames);
- });
- }
- }
- return Promise.resolve([]);
- }
- getLabelNameAndValueForExpression(expr: string, type: string): Promise<any> {
- if (this.labelQueryCache[expr]) {
- return Promise.resolve(this.labelQueryCache[expr]);
- }
- let query = expr;
- if (type === 'metricName') {
- let op = '=~';
- if (/[a-zA-Z_:][a-zA-Z0-9_:]*/.test(expr)) {
- op = '=';
- }
- query = '{__name__' + op + '"' + expr + '"}';
- }
- const { start, end } = this.datasource.getTimeRange();
- const url = '/api/v1/series?match[]=' + encodeURIComponent(query) + '&start=' + start + '&end=' + end;
- return this.datasource.metadataRequest(url).then(response => {
- this.labelQueryCache[expr] = response.data.data;
- return response.data.data;
- });
- }
- transformToCompletions(words, meta) {
- return words.map(name => {
- return {
- caption: name,
- value: name,
- meta: meta,
- score: Number.MAX_VALUE,
- };
- });
- }
- findMetricName(session, row, column) {
- let metricName = '';
- let tokens;
- const nameLabelNameToken = this.findToken(
- session,
- row,
- column,
- 'entity.name.tag.label-matcher',
- '__name__',
- 'paren.lparen.label-matcher'
- );
- if (nameLabelNameToken) {
- tokens = session.getTokens(nameLabelNameToken.row);
- const nameLabelValueToken = tokens[nameLabelNameToken.index + 2];
- if (nameLabelValueToken && nameLabelValueToken.type === 'string.quoted.label-matcher') {
- metricName = nameLabelValueToken.value.slice(1, -1); // cut begin/end quotation
- }
- } else {
- const metricNameToken = this.findToken(session, row, column, 'identifier', null, null);
- if (metricNameToken) {
- tokens = session.getTokens(metricNameToken.row);
- metricName = metricNameToken.value;
- }
- }
- return metricName;
- }
- findToken(session, row, column, target, value, guard) {
- let tokens, idx;
- // find index and get column of previous token
- for (let r = row; r >= 0; r--) {
- let c;
- tokens = session.getTokens(r);
- if (r === row) {
- // current row
- c = 0;
- for (idx = 0; idx < tokens.length; idx++) {
- const nc = c + tokens[idx].value.length;
- if (nc >= column) {
- break;
- }
- c = nc;
- }
- } else {
- idx = tokens.length - 1;
- c =
- _.sum(
- tokens.map(t => {
- return t.value.length;
- })
- ) - tokens[tokens.length - 1].value.length;
- }
- 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].column = c;
- tokens[idx].index = idx;
- return tokens[idx];
- }
- c -= tokens[idx].value.length;
- }
- }
- return null;
- }
- findExpressionMatchedParen(session, row, column) {
- let tokens, idx;
- let deep = 1;
- let expression = ')';
- for (let r = row; r >= 0; r--) {
- tokens = session.getTokens(r);
- if (r === row) {
- // current row
- let 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--) {
- expression = tokens[idx].value + expression;
- if (tokens[idx].type === 'paren.rparen') {
- deep++;
- } else if (tokens[idx].type === 'paren.lparen') {
- deep--;
- if (deep === 0) {
- return expression;
- }
- }
- }
- }
- return expression;
- }
- }
|