CloudWatch: Add syntax highlighting and autocomplete for logs diff command (#111207)

This commit is contained in:
Kevin Yu
2025-10-08 06:44:20 -07:00
committed by GitHub
parent 121663aa16
commit def3b9f5a6
9 changed files with 78 additions and 4 deletions
@@ -4,6 +4,8 @@ import { CustomVariableModel } from '@grafana/data';
import { monacoTypes } from '@grafana/ui';
import { setupMockedTemplateService, logGroupNamesVariable } from '../../../mocks/CloudWatchDataSource';
import { logsTestDataDiffModifierQuery } from '../../../mocks/cloudwatch-logs-test-data/diffModifierQuery';
import { logsTestDataDiffQuery } from '../../../mocks/cloudwatch-logs-test-data/diffQuery';
import { logsTestDataEmptyQuery } from '../../../mocks/cloudwatch-logs-test-data/empty';
import { logsTestDataFilterQuery } from '../../../mocks/cloudwatch-logs-test-data/filterQuery';
import { logsTestDataNewCommandQuery } from '../../../mocks/cloudwatch-logs-test-data/newCommandQuery';
@@ -12,7 +14,7 @@ import { ResourcesAPI } from '../../../resources/ResourcesAPI';
import { ResourceResponse } from '../../../resources/types';
import { LogGroup, LogGroupField } from '../../../types';
import cloudWatchLogsLanguageDefinition, { CLOUDWATCH_LOGS_LANGUAGE_DEFINITION_ID } from '../definition';
import { LOGS_COMMANDS, LOGS_FUNCTION_OPERATORS, SORT_DIRECTION_KEYWORDS, language } from '../language';
import { DIFF_MODIFIERS, LOGS_COMMANDS, LOGS_FUNCTION_OPERATORS, SORT_DIRECTION_KEYWORDS, language } from '../language';
import { LogsCompletionItemProvider } from './CompletionItemProvider';
@@ -81,6 +83,21 @@ describe('LogsCompletionItemProvider', () => {
expect(suggestionLabels).toEqual(expect.arrayContaining(LOGS_FUNCTION_OPERATORS));
});
it('returns diff modifiers after the diff keyword', async () => {
const suggestions = await getSuggestions(logsTestDataDiffQuery.query, logsTestDataDiffQuery.position);
const suggestionLabels = suggestions.map((s) => s.label);
expect(suggestionLabels).toEqual(expect.arrayContaining(DIFF_MODIFIERS));
});
it('returns diff modifiers when partially typed', async () => {
const suggestions = await getSuggestions(
logsTestDataDiffModifierQuery.query,
logsTestDataDiffModifierQuery.position
);
const suggestionLabels = suggestions.map((s) => s.label);
expect(suggestionLabels).toEqual(expect.arrayContaining(DIFF_MODIFIERS));
});
it('returns `in []` snippet for the `in` keyword', async () => {
const suggestions = await getSuggestions(logsTestDataFilterQuery.query, logsTestDataFilterQuery.position);
const suggestionLabels = suggestions.map((s) => s.label);
@@ -8,7 +8,7 @@ import { LinkedToken } from '../../monarch/LinkedToken';
import { TRIGGER_SUGGEST } from '../../monarch/commands';
import { CompletionItem, CompletionItemPriority, StatementPosition, SuggestionKind } from '../../monarch/types';
import { fetchLogGroupFields } from '../../utils';
import { LOGS_COMMANDS, LOGS_FUNCTION_OPERATORS, SORT_DIRECTION_KEYWORDS } from '../language';
import { LOGS_COMMANDS, LOGS_FUNCTION_OPERATORS, SORT_DIRECTION_KEYWORDS, DIFF_MODIFIERS } from '../language';
import { getStatementPosition } from './statementPosition';
import { getSuggestionKinds } from './suggestionKinds';
@@ -115,6 +115,14 @@ export class LogsCompletionItemProvider extends CompletionItemProvider {
});
});
break;
case SuggestionKind.DiffModifier:
DIFF_MODIFIERS.forEach((modifier) => {
addSuggestion(modifier, {
sortText: CompletionItemPriority.High,
kind: monaco.languages.CompletionItemKind.Keyword,
});
});
break;
case SuggestionKind.InKeyword:
addSuggestion('in []', {
insertText: 'in ["$0"]',
@@ -3,6 +3,8 @@ import * as monaco from 'monaco-editor/esm/vs/editor/editor.api';
import { monacoTypes } from '@grafana/ui';
import { logsTestDataCommentOnlyQuery } from '../../../mocks/cloudwatch-logs-test-data/commentOnlyQuery';
import { logsTestDataDiffModifierQuery } from '../../../mocks/cloudwatch-logs-test-data/diffModifierQuery';
import { logsTestDataDiffQuery } from '../../../mocks/cloudwatch-logs-test-data/diffQuery';
import { logsTestDataEmptyQuery } from '../../../mocks/cloudwatch-logs-test-data/empty';
import { logsTestDataMultiLineFullQuery } from '../../../mocks/cloudwatch-logs-test-data/multiLineFullQuery';
import { logsTestDataSingleLineFullQuery } from '../../../mocks/cloudwatch-logs-test-data/singleLineFullQuery';
@@ -164,4 +166,22 @@ describe('getStatementPosition', () => {
getStatementPosition(generateToken(logsTestDataMultiLineFullQuery.query, { lineNumber: 5, column: 3 }))
).toEqual(StatementPosition.Comment);
});
it('should return StatementPosition.DiffKeyword inside the `diff` keyword', () => {
expect(getStatementPosition(generateToken(logsTestDataDiffQuery.query, { lineNumber: 1, column: 22 }))).toEqual(
StatementPosition.DiffKeyword
);
});
it('should return StatementPosition.AfterDiffKeyword after the `diff` keyword', () => {
expect(getStatementPosition(generateToken(logsTestDataDiffQuery.query, { lineNumber: 1, column: 25 }))).toEqual(
StatementPosition.AfterDiffKeyword
);
});
it('should return StatementPosition.DiffModifierArg when typing a diff modifier', () => {
expect(
getStatementPosition(generateToken(logsTestDataDiffModifierQuery.query, { lineNumber: 1, column: 28 }))
).toEqual(StatementPosition.DiffModifierArg);
});
});
@@ -9,6 +9,7 @@ import {
LIMIT,
PARSE,
DEDUP,
DIFF,
LOGS_COMMANDS,
LOGS_FUNCTION_OPERATORS,
LOGS_LOGIC_OPERATORS,
@@ -66,6 +67,8 @@ export const getStatementPosition = (currentToken: LinkedToken | null): Statemen
switch (normalizedCurrentToken) {
case DEDUP:
return StatementPosition.DedupKeyword;
case DIFF:
return StatementPosition.DiffKeyword;
case DISPLAY:
return StatementPosition.DisplayKeyword;
case FIELDS:
@@ -95,6 +98,8 @@ export const getStatementPosition = (currentToken: LinkedToken | null): Statemen
switch (normalizedPreviousNonWhiteSpace) {
case DEDUP:
return StatementPosition.AfterDedupKeyword;
case DIFF:
return StatementPosition.AfterDiffKeyword;
case DISPLAY:
return StatementPosition.AfterDisplayKeyword;
case FIELDS:
@@ -171,6 +176,9 @@ export const getStatementPosition = (currentToken: LinkedToken | null): Statemen
if (nearestKeyword.value === FILTER) {
return StatementPosition.FilterArg;
}
if (nearestKeyword.value === DIFF) {
return StatementPosition.DiffModifierArg;
}
return StatementPosition.CommandArg;
}
@@ -189,6 +197,9 @@ export const getStatementPosition = (currentToken: LinkedToken | null): Statemen
if (nearestKeyword.value === FILTER) {
return StatementPosition.FilterArg;
}
if (nearestKeyword.value === DIFF) {
return StatementPosition.DiffModifierArg;
}
return StatementPosition.CommandArg;
}
@@ -7,6 +7,9 @@ export function getSuggestionKinds(statementPosition: StatementPosition): Sugges
case StatementPosition.AfterSortKeyword:
case StatementPosition.SortArg:
return [SuggestionKind.SortOrderDirectionKeyword, SuggestionKind.Function];
case StatementPosition.AfterDiffKeyword:
case StatementPosition.DiffModifierArg:
return [SuggestionKind.DiffModifier];
case StatementPosition.AfterDisplayKeyword:
case StatementPosition.AfterFieldsKeyword:
case StatementPosition.AfterFilterKeyword:
@@ -7,6 +7,7 @@ interface CloudWatchLogsLanguage extends monacoType.languages.IMonarchLanguage {
builtinFunctions: string[];
}
export const DIFF = 'diff';
export const DISPLAY = 'display';
export const FIELDS = 'fields';
export const FILTER = 'filter';
@@ -16,7 +17,7 @@ export const SORT = 'sort';
export const LIMIT = 'limit';
export const PARSE = 'parse';
export const DEDUP = 'dedup';
export const LOGS_COMMANDS = [DISPLAY, FIELDS, FILTER, PATTERN, STATS, SORT, LIMIT, PARSE, DEDUP];
export const LOGS_COMMANDS = [DISPLAY, FIELDS, FILTER, PATTERN, STATS, SORT, LIMIT, PARSE, DEDUP, DIFF];
export const LOGS_LOGIC_OPERATORS = ['and', 'or', 'not'];
@@ -77,7 +78,8 @@ export const LOGS_FUNCTION_OPERATORS = [
];
export const SORT_DIRECTION_KEYWORDS = ['asc', 'desc'];
export const LOGS_KEYWORDS = ['like', 'by', 'in', 'as', ...SORT_DIRECTION_KEYWORDS];
export const DIFF_MODIFIERS = ['previousDay', 'previousWeek', 'previousMonth'];
export const LOGS_KEYWORDS = ['like', 'by', 'in', 'as', ...SORT_DIRECTION_KEYWORDS, ...DIFF_MODIFIERS];
export const language: CloudWatchLogsLanguage = {
defaultToken: 'invalid',
@@ -103,6 +103,10 @@ export enum StatementPosition {
LikeKeyword,
AfterLikeKeyword,
DiffKeyword,
AfterDiffKeyword,
DiffModifierArg,
Function,
FunctionArg,
CommandArg,
@@ -171,6 +175,7 @@ export enum SuggestionKind {
Command,
Function,
InKeyword,
DiffModifier,
// PPL
BooleanFunction,
@@ -0,0 +1,4 @@
export const logsTestDataDiffModifierQuery = {
query: `pattern @message | diff previous`,
position: { lineNumber: 1, column: 32 },
};
@@ -0,0 +1,4 @@
export const logsTestDataDiffQuery = {
query: `pattern @message | diff `,
position: { lineNumber: 1, column: 24 },
};