From 93f39b5178592c5bfbaa31fc56575dbf8cc6d24b Mon Sep 17 00:00:00 2001 From: Matias Chomicki Date: Wed, 19 Oct 2022 16:30:44 +0200 Subject: [PATCH] Loki Monaco Editor: add missing documentation and new autocompletion type (#57186) * feat(loki-monaco-editor): add documentation and pipe operators - Added documentation for line filters - Added documentation for parsers - Improved parser suggestions - Introduced new PIPE_OPERATION category - Added missing label_format pipe operation * feat(loki-monaco-editor): update tests * Chore: remove unused completion type * Chore: remove line format --- .../completions.test.ts | 19 +++++- .../monaco-completion-provider/completions.ts | 68 ++++++++++++++----- .../monaco-completion-provider/index.ts | 4 +- 3 files changed, 70 insertions(+), 21 deletions(-) diff --git a/public/app/plugins/datasource/loki/components/monaco-query-field/monaco-completion-provider/completions.test.ts b/public/app/plugins/datasource/loki/components/monaco-query-field/monaco-completion-provider/completions.test.ts index 130079d5a82..8e5a93df983 100644 --- a/public/app/plugins/datasource/loki/components/monaco-query-field/monaco-completion-provider/completions.test.ts +++ b/public/app/plugins/datasource/loki/components/monaco-query-field/monaco-completion-provider/completions.test.ts @@ -35,50 +35,59 @@ const otherLabels: Label[] = [ ]; const afterSelectorCompletions = [ { + documentation: 'Log line contains string', insertText: '|= "$0"', isSnippet: true, label: '|= ""', type: 'LINE_FILTER', }, { + documentation: 'Log line does not contain string', insertText: '!= "$0"', isSnippet: true, label: '!= ""', type: 'LINE_FILTER', }, { + documentation: 'Log line contains a match to the regular expression', insertText: '|~ "$0"', isSnippet: true, label: '|~ ""', type: 'LINE_FILTER', }, { + documentation: 'Log line does not contain a match to the regular expression', insertText: '!~ "$0"', isSnippet: true, label: '!~ ""', type: 'LINE_FILTER', }, { + documentation: 'Parse and extract labels from the log content.', insertText: '', label: '// Placeholder for the detected parser', type: 'DETECTED_PARSER_PLACEHOLDER', }, { + documentation: 'Parse and extract labels from the log content.', insertText: '', label: '// Placeholder for logfmt or json', type: 'OPPOSITE_PARSER_PLACEHOLDER', }, { + documentation: 'Parse and extract labels from the log content.', insertText: '| pattern', label: 'pattern', type: 'PARSER', }, { + documentation: 'Parse and extract labels from the log content.', insertText: '| regexp', label: 'regexp', type: 'PARSER', }, { + documentation: 'Parse and extract labels from the log content.', insertText: '| unpack', label: 'unpack', type: 'PARSER', @@ -96,13 +105,19 @@ const afterSelectorCompletions = [ { insertText: '| unwrap', label: 'unwrap', - type: 'LINE_FILTER', + type: 'PIPE_OPERATION', }, { insertText: '| line_format "{{.$0}}"', isSnippet: true, label: 'line_format', - type: 'LINE_FORMAT', + type: 'PIPE_OPERATION', + }, + { + insertText: '| label_format', + isSnippet: true, + label: 'label_format', + type: 'PIPE_OPERATION', }, ]; diff --git a/public/app/plugins/datasource/loki/components/monaco-query-field/monaco-completion-provider/completions.ts b/public/app/plugins/datasource/loki/components/monaco-query-field/monaco-completion-provider/completions.ts index 58f75c372a8..291e9bb1f68 100644 --- a/public/app/plugins/datasource/loki/components/monaco-query-field/monaco-completion-provider/completions.ts +++ b/public/app/plugins/datasource/loki/components/monaco-query-field/monaco-completion-provider/completions.ts @@ -14,7 +14,7 @@ export type CompletionType = | 'PATTERN' | 'PARSER' | 'LINE_FILTER' - | 'LINE_FORMAT'; + | 'PIPE_OPERATION'; type Completion = { type: CompletionType; @@ -64,16 +64,37 @@ const DURATION_COMPLETIONS: Completion[] = ['$__interval', '$__range', '1m', '5m }) ); -function getLineFilterCompletions(afterPipe: boolean): Completion[] { - const lineFilters = afterPipe ? ['=', '~'] : ['|=', '!=', '|~', '!~']; - const prefix = afterPipe ? '|' : ''; +const LINE_FILTER_COMPLETIONS = [ + { + operator: '|=', + documentation: 'Log line contains string', + afterPipe: true, + }, + { + operator: '!=', + documentation: 'Log line does not contain string', + }, + { + operator: '|~', + documentation: 'Log line contains a match to the regular expression', + afterPipe: true, + }, + { + operator: '!~', + documentation: 'Log line does not contain a match to the regular expression', + }, +]; - return lineFilters.map((operator) => ({ - type: 'LINE_FILTER', - label: `${prefix}${operator} ""`, - insertText: `${operator} "$0"`, - isSnippet: true, - })); +function getLineFilterCompletions(afterPipe: boolean): Completion[] { + return LINE_FILTER_COMPLETIONS.filter((completion) => !afterPipe || completion.afterPipe).map( + ({ operator, documentation }) => ({ + type: 'LINE_FILTER', + label: `${operator} ""`, + insertText: `${afterPipe ? operator.replace('|', '') : operator} "$0"`, + isSnippet: true, + documentation, + }) + ); } async function getAllHistoryCompletions(dataProvider: CompletionDataProvider): Promise { @@ -130,33 +151,38 @@ async function getInGroupingCompletions( return getLabelNamesForCompletions('', false, true, otherLabels, dataProvider); } +const PARSERS = ['json', 'logfmt', 'pattern', 'regexp', 'unpack']; +const PARSER_DOCUMENTATION = 'Parse and extract labels from the log content.'; + async function getAfterSelectorCompletions( labels: Label[], afterPipe: boolean, dataProvider: CompletionDataProvider ): Promise { const { extractedLabelKeys, hasJSON, hasLogfmt } = await dataProvider.getParserAndLabelKeys(labels); - const allParsers = new Set(['json', 'logfmt', 'pattern', 'regexp', 'unpack']); + const allParsers = new Set(PARSERS); const completions: Completion[] = []; const prefix = afterPipe ? ' ' : '| '; const hasLevelInExtractedLabels = extractedLabelKeys.some((key) => key === 'level'); if (hasJSON) { allParsers.delete('json'); - const explanation = hasLevelInExtractedLabels ? 'use to get log-levels in the histogram' : 'detected'; + const extra = hasLevelInExtractedLabels ? '' : ' (detected)'; completions.push({ type: 'PARSER', - label: `json (${explanation})`, + label: `json${extra}`, insertText: `${prefix}json`, + documentation: hasLevelInExtractedLabels ? 'Use it to get log-levels in the histogram' : PARSER_DOCUMENTATION, }); } if (hasLogfmt) { allParsers.delete('logfmt'); - const explanation = hasLevelInExtractedLabels ? 'get detected levels in the histogram' : 'detected'; + const extra = hasLevelInExtractedLabels ? '' : ' (detected)'; completions.push({ type: 'DURATION', - label: `logfmt (${explanation})`, + label: `logfmt${extra}`, insertText: `${prefix}logfmt`, + documentation: hasLevelInExtractedLabels ? 'Get detected levels in the histogram' : PARSER_DOCUMENTATION, }); } @@ -166,6 +192,7 @@ async function getAfterSelectorCompletions( type: 'PARSER', label: parser, insertText: `${prefix}${parser}`, + documentation: PARSER_DOCUMENTATION, }); }); @@ -178,18 +205,25 @@ async function getAfterSelectorCompletions( }); completions.push({ - type: 'LINE_FILTER', + type: 'PIPE_OPERATION', label: 'unwrap', insertText: `${prefix}unwrap`, }); completions.push({ - type: 'LINE_FORMAT', + type: 'PIPE_OPERATION', label: 'line_format', insertText: `${prefix}line_format "{{.$0}}"`, isSnippet: true, }); + completions.push({ + type: 'PIPE_OPERATION', + label: 'label_format', + insertText: `${prefix}label_format`, + isSnippet: true, + }); + return [...getLineFilterCompletions(afterPipe), ...completions]; } diff --git a/public/app/plugins/datasource/loki/components/monaco-query-field/monaco-completion-provider/index.ts b/public/app/plugins/datasource/loki/components/monaco-query-field/monaco-completion-provider/index.ts index cb450247142..57fcf1435ed 100644 --- a/public/app/plugins/datasource/loki/components/monaco-query-field/monaco-completion-provider/index.ts +++ b/public/app/plugins/datasource/loki/components/monaco-query-field/monaco-completion-provider/index.ts @@ -47,8 +47,8 @@ function getMonacoCompletionItemKind(type: CompletionType, monaco: Monaco): mona return monaco.languages.CompletionItemKind.Class; case 'LINE_FILTER': return monaco.languages.CompletionItemKind.TypeParameter; - case 'LINE_FORMAT': - return monaco.languages.CompletionItemKind.Event; + case 'PIPE_OPERATION': + return monaco.languages.CompletionItemKind.Interface; default: throw new NeverCaseError(type); }