language_utils.ts 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107
  1. export const RATE_RANGES = ['1m', '5m', '10m', '30m', '1h'];
  2. export const processHistogramLabels = (labels: string[]) => {
  3. const result = [];
  4. const regexp = new RegExp('_bucket($|:)');
  5. for (let index = 0; index < labels.length; index++) {
  6. const label = labels[index];
  7. const isHistogramValue = regexp.test(label);
  8. if (isHistogramValue) {
  9. if (result.indexOf(label) === -1) {
  10. result.push(label);
  11. }
  12. }
  13. }
  14. return { values: { __name__: result } };
  15. };
  16. export function processLabels(labels: any, withName = false) {
  17. const values: { [key: string]: string[] } = {};
  18. labels.forEach((l: any) => {
  19. const { __name__, ...rest } = l;
  20. if (withName) {
  21. values['__name__'] = values['__name__'] || [];
  22. if (values['__name__'].indexOf(__name__) === -1) {
  23. values['__name__'].push(__name__);
  24. }
  25. }
  26. Object.keys(rest).forEach(key => {
  27. if (!values[key]) {
  28. values[key] = [];
  29. }
  30. if (values[key].indexOf(rest[key]) === -1) {
  31. values[key].push(rest[key]);
  32. }
  33. });
  34. });
  35. return { values, keys: Object.keys(values) };
  36. }
  37. // const cleanSelectorRegexp = /\{(\w+="[^"\n]*?")(,\w+="[^"\n]*?")*\}/;
  38. export const selectorRegexp = /\{[^}]*?\}/;
  39. export const labelRegexp = /\b(\w+)(!?=~?)("[^"\n]*?")/g;
  40. export function parseSelector(query: string, cursorOffset = 1): { labelKeys: any[]; selector: string } {
  41. if (!query.match(selectorRegexp)) {
  42. // Special matcher for metrics
  43. if (query.match(/^[A-Za-z:][\w:]*$/)) {
  44. return {
  45. selector: `{__name__="${query}"}`,
  46. labelKeys: ['__name__'],
  47. };
  48. }
  49. throw new Error('Query must contain a selector: ' + query);
  50. }
  51. // Check if inside a selector
  52. const prefix = query.slice(0, cursorOffset);
  53. const prefixOpen = prefix.lastIndexOf('{');
  54. const prefixClose = prefix.lastIndexOf('}');
  55. if (prefixOpen === -1) {
  56. throw new Error('Not inside selector, missing open brace: ' + prefix);
  57. }
  58. if (prefixClose > -1 && prefixClose > prefixOpen) {
  59. throw new Error('Not inside selector, previous selector already closed: ' + prefix);
  60. }
  61. const suffix = query.slice(cursorOffset);
  62. const suffixCloseIndex = suffix.indexOf('}');
  63. const suffixClose = suffixCloseIndex + cursorOffset;
  64. const suffixOpenIndex = suffix.indexOf('{');
  65. const suffixOpen = suffixOpenIndex + cursorOffset;
  66. if (suffixClose === -1) {
  67. throw new Error('Not inside selector, missing closing brace in suffix: ' + suffix);
  68. }
  69. if (suffixOpenIndex > -1 && suffixOpen < suffixClose) {
  70. throw new Error('Not inside selector, next selector opens before this one closed: ' + suffix);
  71. }
  72. // Extract clean labels to form clean selector, incomplete labels are dropped
  73. const selector = query.slice(prefixOpen, suffixClose);
  74. const labels: { [key: string]: { value: string; operator: string } } = {};
  75. selector.replace(labelRegexp, (_, key, operator, value) => {
  76. labels[key] = { value, operator };
  77. return '';
  78. });
  79. // Add metric if there is one before the selector
  80. const metricPrefix = query.slice(0, prefixOpen);
  81. const metricMatch = metricPrefix.match(/[A-Za-z:][\w:]*$/);
  82. if (metricMatch) {
  83. labels['__name__'] = { value: `"${metricMatch[0]}"`, operator: '=' };
  84. }
  85. // Build sorted selector
  86. const labelKeys = Object.keys(labels).sort();
  87. const cleanSelector = labelKeys.map(key => `${key}${labels[key].operator}${labels[key].value}`).join(',');
  88. const selectorString = ['{', cleanSelector, '}'].join('');
  89. return { labelKeys, selector: selectorString };
  90. }
  91. export function expandRecordingRules(query: string, mapping: { [name: string]: string }): string {
  92. const ruleNames = Object.keys(mapping);
  93. const rulesRegex = new RegExp(`(\\s|^)(${ruleNames.join('|')})(\\s|$|\\(|\\[|\\{)`, 'ig');
  94. return query.replace(rulesRegex, (match, pre, name, post) => `${pre}${mapping[name]}${post}`);
  95. }