datasource.ts 4.0 KB

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