From 693bc51693bfe32905fa2e5436921e25f06abe7e Mon Sep 17 00:00:00 2001 From: Matias Chomicki Date: Tue, 17 Jun 2025 23:10:34 +0200 Subject: [PATCH] New Logs Panel: Read default font size from storage + add missing unescaped content check + fix mutation (#106755) * Logs Panel: remove default value to it's taken from local storage code * processing: do not remove newlines, let truncation handle * LogList: check and pass unescaped content to context * LogLine: prevent mutation of searchWords * grammar: use parseFlags * grammar: escape regex --- .../logs/components/panel/LogLine.tsx | 4 +-- .../logs/components/panel/LogList.test.tsx | 28 +++++++++++++++++++ .../logs/components/panel/LogList.tsx | 2 ++ .../features/logs/components/panel/grammar.ts | 22 +++++++++++---- .../logs/components/panel/processing.ts | 4 +-- public/app/plugins/panel/logs/module.tsx | 1 - 6 files changed, 50 insertions(+), 11 deletions(-) diff --git a/public/app/features/logs/components/panel/LogLine.tsx b/public/app/features/logs/components/panel/LogLine.tsx index 9999b946a0e..d6eb485ff42 100644 --- a/public/app/features/logs/components/panel/LogLine.tsx +++ b/public/app/features/logs/components/panel/LogLine.tsx @@ -236,7 +236,7 @@ const DisplayedFields = ({ const { matchingUids, search } = useLogListSearchContext(); const searchWords = useMemo(() => { - const searchWords = log.searchWords && log.searchWords[0] ? log.searchWords : []; + const searchWords = log.searchWords && log.searchWords[0] ? log.searchWords.slice() : []; if (search && matchingUids?.includes(log.uid)) { searchWords.push(search); } @@ -271,7 +271,7 @@ const LogLineBody = ({ log, styles }: { log: LogListModel; styles: LogLineStyles const { matchingUids, search } = useLogListSearchContext(); const highlight = useMemo(() => { - const searchWords = log.searchWords && log.searchWords[0] ? log.searchWords : []; + const searchWords = log.searchWords && log.searchWords[0] ? log.searchWords.slice() : []; if (search && matchingUids?.includes(log.uid)) { searchWords.push(search); } diff --git a/public/app/features/logs/components/panel/LogList.test.tsx b/public/app/features/logs/components/panel/LogList.test.tsx index dbe8dfe78a2..e0cf493bd67 100644 --- a/public/app/features/logs/components/panel/LogList.test.tsx +++ b/public/app/features/logs/components/panel/LogList.test.tsx @@ -259,5 +259,33 @@ describe('LogList', () => { expect(screen.queryByPlaceholderText('Search in logs')).not.toBeInTheDocument(); }); + + test('Does not conflict with search words', async () => { + logs = [ + createLogRow({ uid: '1' }), + createLogRow({ uid: '2', entry: '(?i)some text', searchWords: ['some text'] }), + ]; + + render(); + + expect(screen.queryByPlaceholderText('Search in logs')).not.toBeInTheDocument(); + expect(screen.getByText('log message 1')).toBeInTheDocument(); + expect(screen.getByText('some text')).toBeInTheDocument(); + + await userEvent.keyboard('{Control>}{f}{/Control}'); + + expect(screen.getByPlaceholderText('Search in logs')).toBeInTheDocument(); + + await userEvent.type(screen.getByPlaceholderText('Search in logs'), '(?i)'); + + expect(screen.getByText('log message 1')).toBeInTheDocument(); + expect(screen.getByText('(?i)')).toBeInTheDocument(); + expect(screen.getByText('some text')).toBeInTheDocument(); + + await userEvent.clear(screen.getByPlaceholderText('Search in logs')); + + expect(screen.getByText('log message 1')).toBeInTheDocument(); + expect(screen.getByText('some text')).toBeInTheDocument(); + }); }); }); diff --git a/public/app/features/logs/components/panel/LogList.tsx b/public/app/features/logs/components/panel/LogList.tsx index 5f4cb59b70d..f0f056a1c7b 100644 --- a/public/app/features/logs/components/panel/LogList.tsx +++ b/public/app/features/logs/components/panel/LogList.tsx @@ -151,6 +151,7 @@ export const LogList = ({ timeZone, wrapLogMessage, }: Props) => { + const hasUnescapedContent = useMemo(() => logs.some((log) => log.hasUnescapedContent), [logs]); return ( { - const textMatches = [...highlightWords]; + /** + * 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); + }); if (search) { - textMatches.push(escapeRegex(search)); + expressions.push(new RegExp(escapeRegex(search), 'gi')); } - if (!textMatches.length) { + if (!expressions.length) { return {}; } return { - 'log-search-match': new RegExp(textMatches.join('|'), 'g'), + 'log-search-match': expressions, }; }; + +const cleanNeedle = (needle: string): string => { + return needle.replace(/[[{(][\w,.\/:;<=>?:*+]+$/, ''); +}; diff --git a/public/app/features/logs/components/panel/processing.ts b/public/app/features/logs/components/panel/processing.ts index 5c5b95abbc3..85520b17d4b 100644 --- a/public/app/features/logs/components/panel/processing.ts +++ b/public/app/features/logs/components/panel/processing.ts @@ -88,9 +88,7 @@ export class LogListModel implements LogRowModel { get body(): string { if (this._body === undefined) { - let body = this.collapsed ? this.raw.substring(0, getTruncationLength(null)) : this.raw; - // Turn it into a single-line log entry for the list - this._body = body.replace(/(\r\n|\n|\r)/g, ''); + this._body = this.collapsed ? this.raw.substring(0, getTruncationLength(null)) : this.raw; } return this._body; } diff --git a/public/app/plugins/panel/logs/module.tsx b/public/app/plugins/panel/logs/module.tsx index da674495d77..dd034a995aa 100644 --- a/public/app/plugins/panel/logs/module.tsx +++ b/public/app/plugins/panel/logs/module.tsx @@ -87,7 +87,6 @@ export const plugin = new PanelPlugin(LogsPanel) }, ], }, - defaultValue: 'default', }); }