completer.ts 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. ///<reference path="../../../headers/common.d.ts" />
  2. import {PrometheusDatasource} from "./datasource";
  3. import _ from 'lodash';
  4. export class PromCompleter {
  5. labelQueryCache: any;
  6. labelNameCache: any;
  7. labelValueCache: any;
  8. identifierRegexps = [/[\[\]a-zA-Z_0-9=]/];
  9. constructor(private datasource: PrometheusDatasource) {
  10. this.labelQueryCache = {};
  11. this.labelNameCache = {};
  12. this.labelValueCache = {};
  13. }
  14. getCompletions(editor, session, pos, prefix, callback) {
  15. let token = session.getTokenAt(pos.row, pos.column);
  16. var metricName;
  17. switch (token.type) {
  18. case 'keyword':
  19. metricName = this.findMetricName(session, pos.row, pos.column);
  20. if (!metricName) {
  21. callback(null, this.transformToCompletions(['__name__', 'instance', 'job'], 'label name'));
  22. return;
  23. }
  24. if (this.labelNameCache[metricName]) {
  25. callback(null, this.labelNameCache[metricName]);
  26. return;
  27. }
  28. this.getLabelNameAndValueForMetric(metricName).then(result => {
  29. var labelNames = this.transformToCompletions(
  30. _.uniq(_.flatten(result.map(r => {
  31. return Object.keys(r.metric);
  32. })))
  33. , 'label name');
  34. this.labelNameCache[metricName] = labelNames;
  35. callback(null, labelNames);
  36. });
  37. return;
  38. case 'string':
  39. metricName = this.findMetricName(session, pos.row, pos.column);
  40. if (!metricName) {
  41. callback(null, []);
  42. return;
  43. }
  44. var labelNameToken = this.findToken(session, pos.row, pos.column, 'keyword', null, 'paren.lparen');
  45. if (!labelNameToken) {
  46. callback(null, []);
  47. return;
  48. }
  49. var labelName = labelNameToken.value;
  50. if (this.labelValueCache[metricName] && this.labelValueCache[metricName][labelName]) {
  51. callback(null, this.labelValueCache[metricName][labelName]);
  52. return;
  53. }
  54. this.getLabelNameAndValueForMetric(metricName).then(result => {
  55. var labelValues = this.transformToCompletions(
  56. _.uniq(result.map(r => {
  57. return r.metric[labelName];
  58. }))
  59. , 'label value');
  60. this.labelValueCache[metricName] = this.labelValueCache[metricName] || {};
  61. this.labelValueCache[metricName][labelName] = labelValues;
  62. callback(null, labelValues);
  63. });
  64. return;
  65. }
  66. if (prefix === '[') {
  67. var vectors = [];
  68. for (let unit of ['s', 'm', 'h']) {
  69. for (let value of [1,5,10,30]) {
  70. vectors.push({caption: value+unit, value: '['+value+unit, meta: 'range vector'});
  71. }
  72. }
  73. callback(null, vectors);
  74. return;
  75. }
  76. var query = prefix;
  77. var line = editor.session.getLine(pos.row);
  78. return this.datasource.performSuggestQuery(query, true).then(metricNames => {
  79. callback(null, metricNames.map(name => {
  80. let value = name;
  81. if (prefix === '(') {
  82. value = '(' + name;
  83. }
  84. return {
  85. caption: name,
  86. value: value,
  87. meta: 'metric',
  88. };
  89. }));
  90. });
  91. }
  92. getLabelNameAndValueForMetric(metricName) {
  93. if (this.labelQueryCache[metricName]) {
  94. return Promise.resolve(this.labelQueryCache[metricName]);
  95. }
  96. var op = '=~';
  97. if (/[a-zA-Z_:][a-zA-Z0-9_:]*/.test(metricName)) {
  98. op = '=';
  99. }
  100. var expr = '{__name__' + op + '"' + metricName + '"}';
  101. return this.datasource.performInstantQuery({ expr: expr }, new Date().getTime() / 1000).then(response => {
  102. this.labelQueryCache[metricName] = response.data.data.result;
  103. return response.data.data.result;
  104. });
  105. }
  106. transformToCompletions(words, meta) {
  107. return words.map(name => {
  108. return {
  109. caption: name,
  110. value: name,
  111. meta: meta,
  112. score: Number.MAX_VALUE
  113. };
  114. });
  115. }
  116. findMetricName(session, row, column) {
  117. var metricName = '';
  118. var tokens;
  119. var nameLabelNameToken = this.findToken(session, row, column, 'keyword', '__name__', 'paren.lparen');
  120. if (nameLabelNameToken) {
  121. tokens = session.getTokens(nameLabelNameToken.row);
  122. var nameLabelValueToken = tokens[nameLabelNameToken.index + 2];
  123. if (nameLabelValueToken && nameLabelValueToken.type === 'string') {
  124. metricName = nameLabelValueToken.value.slice(1, -1); // cut begin/end quotation
  125. }
  126. } else {
  127. var metricNameToken = this.findToken(session, row, column, 'identifier', null, null);
  128. if (metricNameToken) {
  129. tokens = session.getTokens(metricNameToken.row);
  130. if (tokens[metricNameToken.index + 1].type === 'paren.lparen') {
  131. metricName = metricNameToken.value;
  132. }
  133. }
  134. }
  135. return metricName;
  136. }
  137. findToken(session, row, column, target, value, guard) {
  138. var tokens, idx;
  139. for (var r = row; r >= 0; r--) {
  140. tokens = session.getTokens(r);
  141. if (r === row) { // current row
  142. var c = 0;
  143. for (idx = 0; idx < tokens.length; idx++) {
  144. c += tokens[idx].value.length;
  145. if (c >= column) {
  146. break;
  147. }
  148. }
  149. } else {
  150. idx = tokens.length - 1;
  151. }
  152. for (; idx >= 0; idx--) {
  153. if (tokens[idx].type === guard) {
  154. return null;
  155. }
  156. if (tokens[idx].type === target
  157. && (!value || tokens[idx].value === value)) {
  158. tokens[idx].row = r;
  159. tokens[idx].index = idx;
  160. return tokens[idx];
  161. }
  162. }
  163. }
  164. return null;
  165. }
  166. }