diff --git a/public/app/features/logs/components/panel/grammar.test.ts b/public/app/features/logs/components/panel/grammar.test.ts index 56c34220377..b3f7aa4a85a 100644 --- a/public/app/features/logs/components/panel/grammar.test.ts +++ b/public/app/features/logs/components/panel/grammar.test.ts @@ -2,7 +2,7 @@ import Prism, { Token } from 'prismjs'; import { createLogLine } from '../mocks/logRow'; -import { generateLogGrammar } from './grammar'; +import { generateLogGrammar, generateTextMatchGrammar } from './grammar'; describe('generateLogGrammar', () => { function generateScenario(entry: string) { @@ -87,3 +87,41 @@ describe('generateLogGrammar', () => { } ); }); + +describe('generateTextMatchGrammar', () => { + const originalErr = console.error; + beforeEach(() => { + jest.spyOn(console, 'error').mockImplementation(() => {}); + }); + afterAll(() => { + console.error = originalErr; + }); + + test('Generates text match grammars for search words', () => { + expect(generateTextMatchGrammar(['search', 'word'])).toEqual({ + 'log-search-match': [/(?:search)/g, /(?:word)/g], + }); + }); + + test('Generates text match grammars for search words and search', () => { + expect(generateTextMatchGrammar(['search', 'word'], 'search text')).toEqual({ + 'log-search-match': [/(?:search)/g, /(?:word)/g, /search text/gi], + }); + }); + + test('Generates text match grammars for regex search words', () => { + expect(generateTextMatchGrammar(['(?i)(TRACE|DEBUG|INFO|WARN|ERROR|OTHER)'])).toEqual({ + 'log-search-match': [/(?:(TRACE|DEBUG|INFO|WARN|ERROR|OTHER))/gi], + }); + }); + + test('Does not throw when the regular expression cannot be parsed correctly', () => { + expect(generateTextMatchGrammar(['(?i)(?PTRACE|DEBUG|INFO|WARN|ERROR|OTHER)'])).toEqual({}); + }); + + test('Handles mixed situations', () => { + expect(generateTextMatchGrammar(['search', '(?i)(TRACE|DEBUG|INFO|WARN|ERROR|OTHER)'], 'word')).toEqual({ + 'log-search-match': [/(?:search)/g, /(?:(TRACE|DEBUG|INFO|WARN|ERROR|OTHER))/gi, /word/gi], + }); + }); +}); diff --git a/public/app/features/logs/components/panel/grammar.ts b/public/app/features/logs/components/panel/grammar.ts index d1f42f33197..3b81ca8f79c 100644 --- a/public/app/features/logs/components/panel/grammar.ts +++ b/public/app/features/logs/components/panel/grammar.ts @@ -52,21 +52,30 @@ export const generateLogGrammar = (log: LogListModel) => { }; }; -export const generateTextMatchGrammar = ( - highlightWords: string[] | undefined = [], - search: string | undefined -): Grammar => { +export const generateTextMatchGrammar = (highlightWords: string[] | undefined = [], search?: string): Grammar => { /** * See: * - https://github.com/grafana/grafana/blob/96f1582c36f94cf4ac7621b7af86bc9e2ad626fb/public/app/features/logs/components/LogRowMessage.tsx#L67 * - https://github.com/grafana/grafana/blob/96f1582c36f94cf4ac7621b7af86bc9e2ad626fb/packages/grafana-data/src/text/text.ts#L12 */ - const expressions = highlightWords.map((word) => { - const { cleaned, flags } = parseFlags(cleanNeedle(word)); - return new RegExp(`(?:${cleaned})`, flags); - }); + const expressions = highlightWords + .map((word) => { + const { cleaned, flags } = parseFlags(cleanNeedle(word)); + try { + return new RegExp(`(?:${cleaned})`, flags); + } catch (e) { + console.error(`generateTextMatchGrammar: cannot generate regular expression from /${cleaned}/${flags}`, e); + } + return undefined; + }) + .filter((expression) => expression !== undefined); + if (search) { - expressions.push(new RegExp(escapeRegex(search), 'gi')); + try { + expressions.push(new RegExp(escapeRegex(search), 'gi')); + } catch (e) { + console.error(`generateTextMatchGrammar: cannot generate regular expression from /${search}/gi`, e); + } } if (!expressions.length) { return {};