diff --git a/public/app/plugins/datasource/loki/querybuilder/operationUtils.test.ts b/public/app/plugins/datasource/loki/querybuilder/operationUtils.test.ts index 82f5c17d86d..ba4e6b8c569 100644 --- a/public/app/plugins/datasource/loki/querybuilder/operationUtils.test.ts +++ b/public/app/plugins/datasource/loki/querybuilder/operationUtils.test.ts @@ -1,4 +1,6 @@ -import { createRangeOperation, createRangeOperationWithGrouping } from './operationUtils'; +import { QueryBuilderOperationDef } from '../../prometheus/querybuilder/shared/types'; + +import { createRangeOperation, createRangeOperationWithGrouping, getLineFilterRenderer } from './operationUtils'; import { LokiVisualQueryOperationCategory } from './types'; describe('createRangeOperation', () => { @@ -122,3 +124,35 @@ describe('createRangeOperationWithGrouping', () => { expect(query).toBe('avg_over_time({job="grafana"} [[$__interval]]) without (source)'); }); }); + +describe('getLineFilterRenderer', () => { + const MOCK_MODEL = { + id: '__line_contains', + params: ['error'], + }; + const MOCK_MODEL_INSENSITIVE = { + id: '__line_contains_case_insensitive', + params: ['ERrOR'], + }; + + const MOCK_DEF = undefined as unknown as QueryBuilderOperationDef; + + const MOCK_INNER_EXPR = '{job="grafana"}'; + + it('getLineFilterRenderer returns a function', () => { + const lineFilterRenderer = getLineFilterRenderer('!~'); + expect(typeof lineFilterRenderer).toBe('function'); + }); + + it('lineFilterRenderer returns the correct query for line contains', () => { + const lineFilterRenderer = getLineFilterRenderer('!~'); + expect(lineFilterRenderer(MOCK_MODEL, MOCK_DEF, MOCK_INNER_EXPR)).toBe('{job="grafana"} !~ `error`'); + }); + + it('lineFilterRenderer returns the correct query for line contains case insensitive', () => { + const lineFilterRenderer = getLineFilterRenderer('!~', true); + expect(lineFilterRenderer(MOCK_MODEL_INSENSITIVE, MOCK_DEF, MOCK_INNER_EXPR)).toBe( + '{job="grafana"} !~ `(?i)ERrOR`' + ); + }); +}); diff --git a/public/app/plugins/datasource/loki/querybuilder/operationUtils.ts b/public/app/plugins/datasource/loki/querybuilder/operationUtils.ts index cac96b0eaca..b9808dbb41a 100644 --- a/public/app/plugins/datasource/loki/querybuilder/operationUtils.ts +++ b/public/app/plugins/datasource/loki/querybuilder/operationUtils.ts @@ -255,8 +255,11 @@ export function addNestedQueryHandler(def: QueryBuilderOperationDef, query: Loki }; } -export function getLineFilterRenderer(operation: string) { +export function getLineFilterRenderer(operation: string, caseInsensitive?: boolean) { return function lineFilterRenderer(model: QueryBuilderOperation, def: QueryBuilderOperationDef, innerExpr: string) { + if (caseInsensitive) { + return `${innerExpr} ${operation} \`(?i)${model.params[0]}\``; + } return `${innerExpr} ${operation} \`${model.params[0]}\``; }; } diff --git a/public/app/plugins/datasource/loki/querybuilder/operations.ts b/public/app/plugins/datasource/loki/querybuilder/operations.ts index 8df076b4d2e..72609bef1fd 100644 --- a/public/app/plugins/datasource/loki/querybuilder/operations.ts +++ b/public/app/plugins/datasource/loki/querybuilder/operations.ts @@ -260,6 +260,50 @@ Example: \`\`error_level=\`level\` \`\` addOperationHandler: addLokiOperation, explainHandler: (op) => `Return log lines that does not contain string \`${op.params[0]}\`.`, }, + { + id: LokiOperationId.LineContainsCaseInsensitive, + name: 'Line contains case insensitive', + params: [ + { + name: 'String', + type: 'string', + hideName: true, + placeholder: 'Text to find', + description: 'Find log lines that contains this text', + minWidth: 33, + runQueryOnEnter: true, + }, + ], + defaultParams: [''], + alternativesKey: 'line filter', + category: LokiVisualQueryOperationCategory.LineFilters, + orderRank: LokiOperationOrder.LineFilters, + renderer: getLineFilterRenderer('|~', true), + addOperationHandler: addLokiOperation, + explainHandler: (op) => `Return log lines that match regex \`(?i)${op.params[0]}\`.`, + }, + { + id: LokiOperationId.LineContainsNotCaseInsensitive, + name: 'Line does not contain case insensitive', + params: [ + { + name: 'String', + type: 'string', + hideName: true, + placeholder: 'Text to exclude', + description: 'Find log lines that does not contain this text', + minWidth: 40, + runQueryOnEnter: true, + }, + ], + defaultParams: [''], + alternativesKey: 'line filter', + category: LokiVisualQueryOperationCategory.LineFilters, + orderRank: LokiOperationOrder.LineFilters, + renderer: getLineFilterRenderer('!~', true), + addOperationHandler: addLokiOperation, + explainHandler: (op) => `Return log lines that does not match regex \`(?i)${op.params[0]}\`.`, + }, { id: LokiOperationId.LineMatchesRegex, name: 'Line contains regex match', diff --git a/public/app/plugins/datasource/loki/querybuilder/types.ts b/public/app/plugins/datasource/loki/querybuilder/types.ts index 12a4a27e580..e4f30107b71 100644 --- a/public/app/plugins/datasource/loki/querybuilder/types.ts +++ b/public/app/plugins/datasource/loki/querybuilder/types.ts @@ -66,6 +66,8 @@ export enum LokiOperationId { BottomK = 'bottomk', LineContains = '__line_contains', LineContainsNot = '__line_contains_not', + LineContainsCaseInsensitive = '__line_contains_case_insensitive', + LineContainsNotCaseInsensitive = '__line_contains_not_case_insensitive', LineMatchesRegex = '__line_matches_regex', LineMatchesRegexNot = '__line_matches_regex_not', LineFilterIpMatches = '__line_filter_ip_matches',