metric_find_query.ts 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. import { chain, map as _map, uniq } from 'lodash';
  2. import { lastValueFrom } from 'rxjs';
  3. import { map } from 'rxjs/operators';
  4. import { MetricFindValue, TimeRange } from '@grafana/data';
  5. import { getTimeSrv } from 'app/features/dashboard/services/TimeSrv';
  6. import { PrometheusDatasource } from './datasource';
  7. import { PromQueryRequest } from './types';
  8. export default class PrometheusMetricFindQuery {
  9. range: TimeRange;
  10. constructor(private datasource: PrometheusDatasource, private query: string) {
  11. this.datasource = datasource;
  12. this.query = query;
  13. this.range = getTimeSrv().timeRange();
  14. }
  15. process(): Promise<MetricFindValue[]> {
  16. const labelNamesRegex = /^label_names\(\)\s*$/;
  17. const labelValuesRegex = /^label_values\((?:(.+),\s*)?([a-zA-Z_][a-zA-Z0-9_]*)\)\s*$/;
  18. const metricNamesRegex = /^metrics\((.+)\)\s*$/;
  19. const queryResultRegex = /^query_result\((.+)\)\s*$/;
  20. const labelNamesQuery = this.query.match(labelNamesRegex);
  21. if (labelNamesQuery) {
  22. return this.labelNamesQuery();
  23. }
  24. const labelValuesQuery = this.query.match(labelValuesRegex);
  25. if (labelValuesQuery) {
  26. if (labelValuesQuery[1]) {
  27. return this.labelValuesQuery(labelValuesQuery[2], labelValuesQuery[1]);
  28. } else {
  29. return this.labelValuesQuery(labelValuesQuery[2]);
  30. }
  31. }
  32. const metricNamesQuery = this.query.match(metricNamesRegex);
  33. if (metricNamesQuery) {
  34. return this.metricNameQuery(metricNamesQuery[1]);
  35. }
  36. const queryResultQuery = this.query.match(queryResultRegex);
  37. if (queryResultQuery) {
  38. return lastValueFrom(this.queryResultQuery(queryResultQuery[1]));
  39. }
  40. // if query contains full metric name, return metric name and label list
  41. return this.metricNameAndLabelsQuery(this.query);
  42. }
  43. labelNamesQuery() {
  44. const start = this.datasource.getPrometheusTime(this.range.from, false);
  45. const end = this.datasource.getPrometheusTime(this.range.to, true);
  46. const params = {
  47. start: start.toString(),
  48. end: end.toString(),
  49. };
  50. const url = `/api/v1/labels`;
  51. return this.datasource.metadataRequest(url, params).then((result: any) => {
  52. return _map(result.data.data, (value) => {
  53. return { text: value };
  54. });
  55. });
  56. }
  57. labelValuesQuery(label: string, metric?: string) {
  58. const start = this.datasource.getPrometheusTime(this.range.from, false);
  59. const end = this.datasource.getPrometheusTime(this.range.to, true);
  60. let url: string;
  61. if (!metric) {
  62. const params = {
  63. start: start.toString(),
  64. end: end.toString(),
  65. };
  66. // return label values globally
  67. url = `/api/v1/label/${label}/values`;
  68. return this.datasource.metadataRequest(url, params).then((result: any) => {
  69. return _map(result.data.data, (value) => {
  70. return { text: value };
  71. });
  72. });
  73. } else {
  74. const params = {
  75. 'match[]': metric,
  76. start: start.toString(),
  77. end: end.toString(),
  78. };
  79. url = `/api/v1/series`;
  80. return this.datasource.metadataRequest(url, params).then((result: any) => {
  81. const _labels = _map(result.data.data, (metric) => {
  82. return metric[label] || '';
  83. }).filter((label) => {
  84. return label !== '';
  85. });
  86. return uniq(_labels).map((metric) => {
  87. return {
  88. text: metric,
  89. expandable: true,
  90. };
  91. });
  92. });
  93. }
  94. }
  95. metricNameQuery(metricFilterPattern: string) {
  96. const start = this.datasource.getPrometheusTime(this.range.from, false);
  97. const end = this.datasource.getPrometheusTime(this.range.to, true);
  98. const params = {
  99. start: start.toString(),
  100. end: end.toString(),
  101. };
  102. const url = `/api/v1/label/__name__/values`;
  103. return this.datasource.metadataRequest(url, params).then((result: any) => {
  104. return chain(result.data.data)
  105. .filter((metricName) => {
  106. const r = new RegExp(metricFilterPattern);
  107. return r.test(metricName);
  108. })
  109. .map((matchedMetricName) => {
  110. return {
  111. text: matchedMetricName,
  112. expandable: true,
  113. };
  114. })
  115. .value();
  116. });
  117. }
  118. queryResultQuery(query: string) {
  119. const end = this.datasource.getPrometheusTime(this.range.to, true);
  120. const instantQuery: PromQueryRequest = { expr: query } as PromQueryRequest;
  121. return this.datasource.performInstantQuery(instantQuery, end).pipe(
  122. map((result) => {
  123. return _map(result.data.data.result, (metricData) => {
  124. let text = metricData.metric.__name__ || '';
  125. delete metricData.metric.__name__;
  126. text +=
  127. '{' +
  128. _map(metricData.metric, (v, k) => {
  129. return k + '="' + v + '"';
  130. }).join(',') +
  131. '}';
  132. text += ' ' + metricData.value[1] + ' ' + metricData.value[0] * 1000;
  133. return {
  134. text: text,
  135. expandable: true,
  136. };
  137. });
  138. })
  139. );
  140. }
  141. metricNameAndLabelsQuery(query: string): Promise<MetricFindValue[]> {
  142. const start = this.datasource.getPrometheusTime(this.range.from, false);
  143. const end = this.datasource.getPrometheusTime(this.range.to, true);
  144. const params = {
  145. 'match[]': query,
  146. start: start.toString(),
  147. end: end.toString(),
  148. };
  149. const url = `/api/v1/series`;
  150. const self = this;
  151. return this.datasource.metadataRequest(url, params).then((result: any) => {
  152. return _map(result.data.data, (metric: { [key: string]: string }) => {
  153. return {
  154. text: self.datasource.getOriginalMetricName(metric),
  155. expandable: true,
  156. };
  157. });
  158. });
  159. }
  160. }