datasource.ts 4.3 KB

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