datasource.ts 3.7 KB

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