datasource.ts 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. import _ from 'lodash';
  2. import * as dateMath from 'app/core/utils/datemath';
  3. import { processStreams } from './result_transformer';
  4. const DEFAULT_LIMIT = 100;
  5. const DEFAULT_QUERY_PARAMS = {
  6. direction: 'BACKWARD',
  7. limit: DEFAULT_LIMIT,
  8. regexp: '',
  9. query: '',
  10. };
  11. const QUERY_REGEXP = /({\w+="[^"]+"})?\s*(\w[^{]+)?\s*({\w+="[^"]+"})?/;
  12. export function parseQuery(input: string) {
  13. const match = input.match(QUERY_REGEXP);
  14. let query = '';
  15. let regexp = '';
  16. if (match) {
  17. if (match[1]) {
  18. query = match[1];
  19. }
  20. if (match[2]) {
  21. regexp = match[2].trim();
  22. }
  23. if (match[3]) {
  24. if (match[1]) {
  25. query = `${match[1].slice(0, -1)},${match[3].slice(1)}`;
  26. } else {
  27. query = match[3];
  28. }
  29. }
  30. }
  31. return { query, regexp };
  32. }
  33. function serializeParams(data: any) {
  34. return Object.keys(data)
  35. .map(k => {
  36. const v = data[k];
  37. return encodeURIComponent(k) + '=' + encodeURIComponent(v);
  38. })
  39. .join('&');
  40. }
  41. export default class LoggingDatasource {
  42. /** @ngInject */
  43. constructor(private instanceSettings, private backendSrv, private templateSrv) {}
  44. _request(apiUrl: string, data?, options?: any) {
  45. const baseUrl = this.instanceSettings.url;
  46. const params = data ? serializeParams(data) : '';
  47. const url = `${baseUrl}${apiUrl}?${params}`;
  48. const req = {
  49. ...options,
  50. url,
  51. };
  52. return this.backendSrv.datasourceRequest(req);
  53. }
  54. prepareQueryTarget(target, options) {
  55. const interpolated = this.templateSrv.replace(target.expr);
  56. const start = this.getTime(options.range.from, false);
  57. const end = this.getTime(options.range.to, true);
  58. return {
  59. ...DEFAULT_QUERY_PARAMS,
  60. ...parseQuery(interpolated),
  61. start,
  62. end,
  63. };
  64. }
  65. query(options) {
  66. const queryTargets = options.targets
  67. .filter(target => target.expr)
  68. .map(target => this.prepareQueryTarget(target, options));
  69. if (queryTargets.length === 0) {
  70. return Promise.resolve({ data: [] });
  71. }
  72. const queries = queryTargets.map(target => this._request('/api/prom/query', target));
  73. return Promise.all(queries).then((results: any[]) => {
  74. // Flatten streams from multiple queries
  75. const allStreams = results.reduce((acc, response, i) => {
  76. const streams = response.data.streams || [];
  77. // Inject search for match highlighting
  78. const search = queryTargets[i].regexp;
  79. streams.forEach(s => {
  80. s.search = search;
  81. });
  82. return [...acc, ...streams];
  83. }, []);
  84. const model = processStreams(allStreams, DEFAULT_LIMIT);
  85. return { data: model };
  86. });
  87. }
  88. metadataRequest(url) {
  89. // HACK to get label values for {job=|}, will be replaced when implementing LoggingQueryField
  90. const apiUrl = url.replace('v1', 'prom');
  91. return this._request(apiUrl, { silent: true }).then(res => {
  92. const data = { data: { data: res.data.values || [] } };
  93. return data;
  94. });
  95. }
  96. getTime(date, roundUp) {
  97. if (_.isString(date)) {
  98. date = dateMath.parse(date, roundUp);
  99. }
  100. return Math.ceil(date.valueOf() * 1e6);
  101. }
  102. testDatasource() {
  103. return this._request('/api/prom/label')
  104. .then(res => {
  105. if (res && res.data && res.data.values && res.data.values.length > 0) {
  106. return { status: 'success', message: 'Data source connected and labels found.' };
  107. }
  108. return {
  109. status: 'error',
  110. message: 'Data source connected, but no labels received. Verify that logging is configured properly.',
  111. };
  112. })
  113. .catch(err => {
  114. return { status: 'error', message: err.message };
  115. });
  116. }
  117. }