| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221 |
- // Libraries
- import _ from 'lodash';
- // Services & Utils
- import * as dateMath from '@grafana/ui/src/utils/datemath';
- import { addLabelToSelector } from 'app/plugins/datasource/prometheus/add_label_to_query';
- import LanguageProvider from './language_provider';
- import { logStreamToSeriesData } from './result_transformer';
- import { formatQuery, parseQuery } from './query_utils';
- // Types
- import {
- PluginMeta,
- DataQueryRequest,
- SeriesData,
- DataSourceApi,
- DataSourceInstanceSettings,
- DataQueryError,
- } from '@grafana/ui/src/types';
- import { LokiQuery, LokiOptions } from './types';
- import { BackendSrv } from 'app/core/services/backend_srv';
- import { TemplateSrv } from 'app/features/templating/template_srv';
- import { safeStringifyValue } from 'app/core/utils/explore';
- export const DEFAULT_MAX_LINES = 1000;
- const DEFAULT_QUERY_PARAMS = {
- direction: 'BACKWARD',
- limit: DEFAULT_MAX_LINES,
- regexp: '',
- query: '',
- };
- function serializeParams(data: any) {
- return Object.keys(data)
- .map(k => {
- const v = data[k];
- return encodeURIComponent(k) + '=' + encodeURIComponent(v);
- })
- .join('&');
- }
- export class LokiDatasource extends DataSourceApi<LokiQuery, LokiOptions> {
- languageProvider: LanguageProvider;
- maxLines: number;
- /** @ngInject */
- constructor(
- private instanceSettings: DataSourceInstanceSettings<LokiOptions>,
- private backendSrv: BackendSrv,
- private templateSrv: TemplateSrv
- ) {
- super(instanceSettings);
- this.languageProvider = new LanguageProvider(this);
- const settingsData = instanceSettings.jsonData || {};
- this.maxLines = parseInt(settingsData.maxLines, 10) || DEFAULT_MAX_LINES;
- }
- _request(apiUrl: string, data?, options?: any) {
- const baseUrl = this.instanceSettings.url;
- const params = data ? serializeParams(data) : '';
- const url = `${baseUrl}${apiUrl}?${params}`;
- const req = {
- ...options,
- url,
- };
- return this.backendSrv.datasourceRequest(req);
- }
- prepareQueryTarget(target: LokiQuery, options: DataQueryRequest<LokiQuery>) {
- const interpolated = this.templateSrv.replace(target.expr);
- const start = this.getTime(options.range.from, false);
- const end = this.getTime(options.range.to, true);
- const refId = target.refId;
- return {
- ...DEFAULT_QUERY_PARAMS,
- ...parseQuery(interpolated),
- start,
- end,
- limit: this.maxLines,
- refId,
- };
- }
- async query(options: DataQueryRequest<LokiQuery>) {
- const queryTargets = options.targets
- .filter(target => target.expr && !target.hide)
- .map(target => this.prepareQueryTarget(target, options));
- if (queryTargets.length === 0) {
- return Promise.resolve({ data: [] });
- }
- const queries = queryTargets.map(target =>
- this._request('/api/prom/query', target).catch((err: any) => {
- if (err.cancelled) {
- return err;
- }
- const error: DataQueryError = {
- message: 'Unknown error during query transaction. Please check JS console logs.',
- refId: target.refId,
- };
- if (err.data) {
- if (typeof err.data === 'string') {
- error.message = err.data;
- } else if (err.data.error) {
- error.message = safeStringifyValue(err.data.error);
- }
- } else if (err.message) {
- error.message = err.message;
- } else if (typeof err === 'string') {
- error.message = err;
- }
- error.status = err.status;
- error.statusText = err.statusText;
- throw error;
- })
- );
- return Promise.all(queries).then((results: any[]) => {
- const series: Array<SeriesData | DataQueryError> = [];
- for (let i = 0; i < results.length; i++) {
- const result = results[i];
- if (result.data) {
- const refId = queryTargets[i].refId;
- for (const stream of result.data.streams || []) {
- const seriesData = logStreamToSeriesData(stream);
- seriesData.refId = refId;
- seriesData.meta = {
- search: queryTargets[i].regexp,
- limit: this.maxLines,
- };
- series.push(seriesData);
- }
- }
- }
- return { data: series };
- });
- }
- async importQueries(queries: LokiQuery[], originMeta: PluginMeta): Promise<LokiQuery[]> {
- return this.languageProvider.importQueries(queries, originMeta.id);
- }
- metadataRequest(url) {
- // HACK to get label values for {job=|}, will be replaced when implementing LokiQueryField
- const apiUrl = url.replace('v1', 'prom');
- return this._request(apiUrl, { silent: true }).then(res => {
- const data = { data: { data: res.data.values || [] } };
- return data;
- });
- }
- modifyQuery(query: LokiQuery, action: any): LokiQuery {
- const parsed = parseQuery(query.expr || '');
- let selector = parsed.query;
- switch (action.type) {
- case 'ADD_FILTER': {
- selector = addLabelToSelector(selector, action.key, action.value);
- break;
- }
- default:
- break;
- }
- const expression = formatQuery(selector, parsed.regexp);
- return { ...query, expr: expression };
- }
- getHighlighterExpression(query: LokiQuery): string {
- return parseQuery(query.expr).regexp;
- }
- getTime(date, roundUp) {
- if (_.isString(date)) {
- date = dateMath.parse(date, roundUp);
- }
- return Math.ceil(date.valueOf() * 1e6);
- }
- testDatasource() {
- return this._request('/api/prom/label')
- .then(res => {
- if (res && res.data && res.data.values && res.data.values.length > 0) {
- return { status: 'success', message: 'Data source connected and labels found.' };
- }
- return {
- status: 'error',
- message:
- 'Data source connected, but no labels received. Verify that Loki and Promtail is configured properly.',
- };
- })
- .catch(err => {
- let message = 'Loki: ';
- if (err.statusText) {
- message += err.statusText;
- } else {
- message += 'Cannot connect to Loki';
- }
- if (err.status) {
- message += `. ${err.status}`;
- }
- if (err.data && err.data.message) {
- message += `. ${err.data.message}`;
- } else if (err.data) {
- message += `. ${err.data}`;
- }
- return { status: 'error', message: message };
- });
- }
- }
- export default LokiDatasource;
|