diff --git a/public/app/plugins/datasource/loki/datasource.test.ts b/public/app/plugins/datasource/loki/datasource.test.ts index 6ba91a80caf..5ad1792dc65 100644 --- a/public/app/plugins/datasource/loki/datasource.test.ts +++ b/public/app/plugins/datasource/loki/datasource.test.ts @@ -590,6 +590,96 @@ describe('LokiDatasource', () => { expect(res).toEqual([]); }); }); + + describe('modifyQuery', () => { + describe('when called with ADD_FILTER', () => { + describe('and query has no parser', () => { + it('then the correct label should be added for logs query', () => { + const query: LokiQuery = { refId: 'A', expr: '{bar="baz"}' }; + const action = { key: 'job', value: 'grafana', type: 'ADD_FILTER' }; + const ds = createLokiDSForTests(); + const result = ds.modifyQuery(query, action); + + expect(result.refId).toEqual('A'); + expect(result.expr).toEqual('{bar="baz",job="grafana"}'); + }); + + it('then the correct label should be added for metrics query', () => { + const query: LokiQuery = { refId: 'A', expr: 'rate({bar="baz"}[5m])' }; + const action = { key: 'job', value: 'grafana', type: 'ADD_FILTER' }; + const ds = createLokiDSForTests(); + const result = ds.modifyQuery(query, action); + + expect(result.refId).toEqual('A'); + expect(result.expr).toEqual('rate({bar="baz",job="grafana"}[5m])'); + }); + describe('and query has parser', () => { + it('then the correct label should be added for logs query', () => { + const query: LokiQuery = { refId: 'A', expr: '{bar="baz"} | logfmt' }; + const action = { key: 'job', value: 'grafana', type: 'ADD_FILTER' }; + const ds = createLokiDSForTests(); + const result = ds.modifyQuery(query, action); + + expect(result.refId).toEqual('A'); + expect(result.expr).toEqual('{bar="baz"} | logfmt | job="grafana"'); + }); + it('then the correct label should be added for metrics query', () => { + const query: LokiQuery = { refId: 'A', expr: 'rate({bar="baz"} | logfmt [5m])' }; + const action = { key: 'job', value: 'grafana', type: 'ADD_FILTER' }; + const ds = createLokiDSForTests(); + const result = ds.modifyQuery(query, action); + + expect(result.refId).toEqual('A'); + expect(result.expr).toEqual('rate({bar="baz",job="grafana"} | logfmt [5m])'); + }); + }); + }); + }); + + describe('when called with ADD_FILTER_OUT', () => { + describe('and query has no parser', () => { + it('then the correct label should be added for logs query', () => { + const query: LokiQuery = { refId: 'A', expr: '{bar="baz"}' }; + const action = { key: 'job', value: 'grafana', type: 'ADD_FILTER_OUT' }; + const ds = createLokiDSForTests(); + const result = ds.modifyQuery(query, action); + + expect(result.refId).toEqual('A'); + expect(result.expr).toEqual('{bar="baz",job!="grafana"}'); + }); + + it('then the correct label should be added for metrics query', () => { + const query: LokiQuery = { refId: 'A', expr: 'rate({bar="baz"}[5m])' }; + const action = { key: 'job', value: 'grafana', type: 'ADD_FILTER_OUT' }; + const ds = createLokiDSForTests(); + const result = ds.modifyQuery(query, action); + + expect(result.refId).toEqual('A'); + expect(result.expr).toEqual('rate({bar="baz",job!="grafana"}[5m])'); + }); + describe('and query has parser', () => { + it('then the correct label should be added for logs query', () => { + const query: LokiQuery = { refId: 'A', expr: '{bar="baz"} | logfmt' }; + const action = { key: 'job', value: 'grafana', type: 'ADD_FILTER_OUT' }; + const ds = createLokiDSForTests(); + const result = ds.modifyQuery(query, action); + + expect(result.refId).toEqual('A'); + expect(result.expr).toEqual('{bar="baz"} | logfmt | job!="grafana"'); + }); + it('then the correct label should be added for metrics query', () => { + const query: LokiQuery = { refId: 'A', expr: 'rate({bar="baz"} | logfmt [5m])' }; + const action = { key: 'job', value: 'grafana', type: 'ADD_FILTER_OUT' }; + const ds = createLokiDSForTests(); + const result = ds.modifyQuery(query, action); + + expect(result.refId).toEqual('A'); + expect(result.expr).toEqual('rate({bar="baz",job!="grafana"} | logfmt [5m])'); + }); + }); + }); + }); + }); }); function createLokiDSForTests( diff --git a/public/app/plugins/datasource/loki/datasource.ts b/public/app/plugins/datasource/loki/datasource.ts index aa6d708ae9f..c20b9bce56d 100644 --- a/public/app/plugins/datasource/loki/datasource.ts +++ b/public/app/plugins/datasource/loki/datasource.ts @@ -34,7 +34,7 @@ import { lokiStreamsToDataFrames, processRangeQueryResponse, } from './result_transformer'; -import { getHighlighterExpressionsFromQuery } from './query_utils'; +import { getHighlighterExpressionsFromQuery, queryHasPipeParser, addParsedLabelToQuery } from './query_utils'; import { LokiOptions, @@ -380,11 +380,21 @@ export class LokiDatasource extends DataSourceApi { let expression = query.expr ?? ''; switch (action.type) { case 'ADD_FILTER': { - expression = addLabelToQuery(expression, action.key, action.value, undefined, true); + // Temporary fix for log queries that use parser. We don't know which labels are parsed and which are actual labels. + // If query has parser, we treat all labels as parsed and use | key="value" syntax (same in ADD_FILTER_OUT) + if (queryHasPipeParser(expression) && !isMetricsQuery(expression)) { + expression = addParsedLabelToQuery(expression, action.key, action.value, '='); + } else { + expression = addLabelToQuery(expression, action.key, action.value, undefined, true); + } break; } case 'ADD_FILTER_OUT': { - expression = addLabelToQuery(expression, action.key, action.value, '!=', true); + if (queryHasPipeParser(expression) && !isMetricsQuery(expression)) { + expression = addParsedLabelToQuery(expression, action.key, action.value, '!='); + } else { + expression = addLabelToQuery(expression, action.key, action.value, '!=', true); + } break; } default: diff --git a/public/app/plugins/datasource/loki/query_utils.ts b/public/app/plugins/datasource/loki/query_utils.ts index 7aaa5f5ce28..e8dd3353469 100644 --- a/public/app/plugins/datasource/loki/query_utils.ts +++ b/public/app/plugins/datasource/loki/query_utils.ts @@ -1,4 +1,5 @@ import { escapeRegExp } from 'lodash'; +import { PIPE_PARSERS } from './syntax'; export function formatQuery(selector: string | undefined): string { return `${selector || ''}`.trim(); @@ -50,3 +51,13 @@ export function getHighlighterExpressionsFromQuery(input: string): string[] { return results; } + +export function queryHasPipeParser(expr: string): boolean { + const parsers = PIPE_PARSERS.map((parser) => `${parser.label}`).join('|'); + const regexp = new RegExp(`\\\|\\\s?(${parsers})`); + return regexp.test(expr); +} + +export function addParsedLabelToQuery(expr: string, key: string, value: string | number, operator: string) { + return expr + ` | ${key}${operator}"${value.toString()}"`; +}