Grammar: wrap in try/catch in case regex creation fails (#112595)

* Grammar: wrap in try/catch in case regex creation fails

* Improve breadcrumbs
This commit is contained in:
Matias Chomicki
2025-10-17 15:27:24 +00:00
committed by GitHub
parent 8c97ba4f08
commit bbb5322008
2 changed files with 57 additions and 10 deletions
@@ -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)(?P<filtered_log_level>TRACE|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],
});
});
});
@@ -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 {};