Просмотр исходного кода

(prometheus) support label name completion

Mitsuhiro Tanda 8 лет назад
Родитель
Сommit
40b74e6664

+ 97 - 8
public/app/plugins/datasource/prometheus/completer.ts

@@ -1,11 +1,15 @@
 ///<reference path="../../../headers/common.d.ts" />
 
 import {PrometheusDatasource} from "./datasource";
+import _ from 'lodash';
 
 export class PromCompleter {
+  labelNameCache: any;
+
   identifierRegexps = [/[\[\]a-zA-Z_0-9=]/];
 
   constructor(private datasource: PrometheusDatasource) {
+    this.labelNameCache = {};
   }
 
   getCompletions(editor, session, pos, prefix, callback) {
@@ -13,14 +17,31 @@ export class PromCompleter {
 
     switch (token.type) {
       case 'label.name':
-        callback(null, ['instance', 'job'].map(function (key) {
-          return {
-            caption: key,
-            value: key,
-            meta: "label name",
-            score: Number.MAX_VALUE
-          };
-        }));
+        var metricName = this.findMetricName(session, pos.row, pos.column);
+        if (!metricName) {
+          callback(null, this.transformToCompletions(['__name__', 'instance', 'job']));
+          return;
+        }
+
+        if (this.labelNameCache[metricName]) {
+          callback(null, this.labelNameCache[metricName]);
+          return;
+        }
+
+        var op = '=~';
+        if (/[a-zA-Z_:][a-zA-Z0-9_:]*/.test(metricName)) {
+          op = '=';
+        }
+        var expr = '{__name__' + op + '"' + metricName + '"}';
+        this.datasource.performInstantQuery({ expr: expr }, new Date().getTime() / 1000).then(response => {
+          var labelNames = this.transformToCompletions(
+            _.uniq(_.flatten(response.data.data.result.map(r => {
+              return Object.keys(r.metric);
+            })))
+          );
+          this.labelNameCache[metricName] = labelNames;
+          callback(null, labelNames);
+        });
         return;
       case 'label.value':
         callback(null, []);
@@ -57,4 +78,72 @@ export class PromCompleter {
     });
   }
 
+  transformToCompletions(words) {
+    return words.map(name => {
+      return {
+        caption: name,
+        value: name,
+        meta: "label name",
+        score: Number.MAX_VALUE
+      };
+    });
+  }
+
+  findMetricName(session, row, column) {
+    var metricName = '';
+
+    var tokens;
+    var nameLabelNameToken = this.findToken(session, row, column, 'label.name', '__name__', 'paren.lparen');
+    if (nameLabelNameToken) {
+      tokens = session.getTokens(nameLabelNameToken.row);
+      var nameLabelValueToken = tokens[nameLabelNameToken.index + 2];
+      if (nameLabelValueToken && nameLabelValueToken.type === 'label.value') {
+        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;
+  }
+
 }

+ 5 - 0
public/app/plugins/datasource/prometheus/datasource.ts

@@ -158,6 +158,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';