mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Datasource/CloudWatch: Results of CloudWatch Logs stats queries are now grouped (#24396)
* Datasource/CloudWatch: Results of CloudWatch Logs stats queries are now grouped
This commit is contained in:
@@ -11,7 +11,6 @@ import {
|
||||
BracesPlugin,
|
||||
Select,
|
||||
MultiSelect,
|
||||
Token,
|
||||
} from '@grafana/ui';
|
||||
|
||||
// Utils & Services
|
||||
@@ -143,15 +142,37 @@ export class CloudWatchLogsQueryField extends React.PureComponent<CloudWatchLogs
|
||||
|
||||
onChangeQuery = (value: string, override?: boolean) => {
|
||||
// Send text change to parent
|
||||
const { query, onChange, onRunQuery } = this.props;
|
||||
const { query, onChange, onRunQuery, datasource, exploreMode } = this.props;
|
||||
const { selectedLogGroups, selectedRegion } = this.state;
|
||||
|
||||
// TEMP: Remove when logs/metrics unification is complete
|
||||
if (datasource.languageProvider && exploreMode === ExploreMode.Logs) {
|
||||
const cloudwatchLanguageProvider = datasource.languageProvider as CloudWatchLanguageProvider;
|
||||
const queryUsesStatsCommand = cloudwatchLanguageProvider.isStatsQuery(query.expression);
|
||||
|
||||
if (queryUsesStatsCommand) {
|
||||
this.setState({
|
||||
hint: {
|
||||
message: 'You are trying to run a stats query in Logs mode. ',
|
||||
fix: {
|
||||
label: 'Switch to Metrics mode.',
|
||||
action: this.switchToMetrics,
|
||||
},
|
||||
},
|
||||
});
|
||||
} else {
|
||||
this.setState({
|
||||
hint: undefined,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (onChange) {
|
||||
const nextQuery = {
|
||||
...query,
|
||||
expression: value,
|
||||
logGroupNames: selectedLogGroups?.map(logGroupName => logGroupName.value) ?? [],
|
||||
region: selectedRegion.value,
|
||||
logGroupNames: selectedLogGroups?.map(logGroupName => logGroupName.value!) ?? [],
|
||||
region: selectedRegion.value ?? 'default',
|
||||
};
|
||||
onChange(nextQuery);
|
||||
|
||||
@@ -171,7 +192,7 @@ export class CloudWatchLogsQueryField extends React.PureComponent<CloudWatchLogs
|
||||
if (onChange) {
|
||||
const nextQuery = {
|
||||
...query,
|
||||
logGroupNames: v.map(logGroupName => logGroupName.value) ?? [],
|
||||
logGroupNames: v.map(logGroupName => logGroupName.value!) ?? [],
|
||||
};
|
||||
|
||||
onChange(nextQuery);
|
||||
@@ -193,8 +214,8 @@ export class CloudWatchLogsQueryField extends React.PureComponent<CloudWatchLogs
|
||||
if (onChange) {
|
||||
const nextQuery = {
|
||||
...query,
|
||||
region: v.value,
|
||||
logGroupNames: selectedLogGroups.map(group => group.value),
|
||||
region: v.value ?? 'default',
|
||||
logGroupNames: selectedLogGroups.map(group => group.value!),
|
||||
};
|
||||
|
||||
onChange(nextQuery);
|
||||
@@ -208,7 +229,7 @@ export class CloudWatchLogsQueryField extends React.PureComponent<CloudWatchLogs
|
||||
};
|
||||
|
||||
onTypeahead = async (typeahead: TypeaheadInput): Promise<TypeaheadOutput> => {
|
||||
const { datasource, exploreMode } = this.props;
|
||||
const { datasource } = this.props;
|
||||
const { selectedLogGroups } = this.state;
|
||||
|
||||
if (!datasource.languageProvider) {
|
||||
@@ -224,23 +245,6 @@ export class CloudWatchLogsQueryField extends React.PureComponent<CloudWatchLogs
|
||||
{ history, absoluteRange, logGroupNames: selectedLogGroups.map(logGroup => logGroup.value!) }
|
||||
);
|
||||
|
||||
const tokens = editor?.value.data.get('tokens');
|
||||
const queryUsesStatsCommand = tokens.find(
|
||||
(token: Token) => token.types.includes('query-command') && token.content.toLowerCase() === 'stats'
|
||||
);
|
||||
|
||||
// TEMP: Remove when logs/metrics unification is complete
|
||||
if (queryUsesStatsCommand && exploreMode === ExploreMode.Logs) {
|
||||
this.setState({
|
||||
hint: {
|
||||
message: 'You are trying to run a stats query in Logs mode. ',
|
||||
fix: {
|
||||
label: 'Switch to Metrics mode.',
|
||||
action: this.switchToMetrics,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
|
||||
@@ -76,7 +76,7 @@ export class CloudWatchDatasource extends DataSourceApi<CloudWatchQuery, CloudWa
|
||||
datasourceName: string;
|
||||
debouncedAlert: (datasourceName: string, region: string) => void;
|
||||
debouncedCustomAlert: (title: string, message: string) => void;
|
||||
logQueries: Set<{ id: string; region: string }>;
|
||||
logQueries: Record<string, { id: string; region: string }>;
|
||||
languageProvider: CloudWatchLanguageProvider;
|
||||
|
||||
/** @ngInject */
|
||||
@@ -93,7 +93,7 @@ export class CloudWatchDatasource extends DataSourceApi<CloudWatchQuery, CloudWa
|
||||
this.standardStatistics = ['Average', 'Maximum', 'Minimum', 'Sum', 'SampleCount'];
|
||||
this.debouncedAlert = memoizedDebounce(displayAlert, AppNotificationTimeout.Error);
|
||||
this.debouncedCustomAlert = memoizedDebounce(displayCustomError, AppNotificationTimeout.Error);
|
||||
this.logQueries = new Set<{ id: string; region: string }>();
|
||||
this.logQueries = {};
|
||||
|
||||
this.languageProvider = new CloudWatchLanguageProvider(this);
|
||||
}
|
||||
@@ -131,7 +131,10 @@ export class CloudWatchDatasource extends DataSourceApi<CloudWatchQuery, CloudWa
|
||||
dataFrames.map(dataFrame => ({
|
||||
queryId: dataFrame.fields[0].values.get(0),
|
||||
region: dataFrame.meta?.custom?.['Region'] ?? 'default',
|
||||
refId: dataFrame.refId,
|
||||
refId: dataFrame.refId!,
|
||||
groupResults: this.languageProvider.isStatsQuery(
|
||||
options.targets.find(target => target.refId === dataFrame.refId)!.expression
|
||||
),
|
||||
}))
|
||||
)
|
||||
),
|
||||
@@ -196,9 +199,13 @@ export class CloudWatchDatasource extends DataSourceApi<CloudWatchQuery, CloudWa
|
||||
return this.performTimeSeriesQuery(request, options.range);
|
||||
}
|
||||
|
||||
logsQuery(queryParams: Array<{ queryId: string; limit?: number; region: string }>): Observable<DataQueryResponse> {
|
||||
this.logQueries.clear();
|
||||
queryParams.forEach(param => this.logQueries.add({ id: param.queryId, region: param.region }));
|
||||
logsQuery(
|
||||
queryParams: Array<{ queryId: string; refId: string; limit?: number; region: string; groupResults?: boolean }>
|
||||
): Observable<DataQueryResponse> {
|
||||
this.logQueries = {};
|
||||
queryParams.forEach(param => {
|
||||
this.logQueries[param.refId] = { id: param.queryId, region: param.region };
|
||||
});
|
||||
let prevRecordsMatched: Record<string, number> = {};
|
||||
|
||||
return withTeardown(
|
||||
@@ -238,9 +245,10 @@ export class CloudWatchDatasource extends DataSourceApi<CloudWatchQuery, CloudWa
|
||||
CloudWatchLogsQueryStatus.Complete,
|
||||
CloudWatchLogsQueryStatus.Cancelled,
|
||||
CloudWatchLogsQueryStatus.Failed,
|
||||
].includes(dataframe.meta?.custom?.['Status'])
|
||||
].includes(dataframe.meta?.custom?.['Status']) &&
|
||||
this.logQueries.hasOwnProperty(dataframe.refId!)
|
||||
) {
|
||||
this.logQueries.delete({ id: queryParams[i].queryId, region: queryParams[i].region });
|
||||
delete this.logQueries[dataframe.refId!];
|
||||
}
|
||||
});
|
||||
}),
|
||||
@@ -299,13 +307,17 @@ export class CloudWatchDatasource extends DataSourceApi<CloudWatchQuery, CloudWa
|
||||
}
|
||||
|
||||
stopQueries() {
|
||||
if (this.logQueries.size > 0) {
|
||||
if (Object.keys(this.logQueries).length > 0) {
|
||||
this.makeLogActionRequest(
|
||||
'StopQuery',
|
||||
[...this.logQueries.values()].map(logQuery => ({ queryId: logQuery.id, region: logQuery.region })),
|
||||
Object.values(this.logQueries).map(logQuery => ({ queryId: logQuery.id, region: logQuery.region })),
|
||||
undefined,
|
||||
false
|
||||
).pipe(finalize(() => this.logQueries.clear()));
|
||||
).pipe(
|
||||
finalize(() => {
|
||||
this.logQueries = {};
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ import { AbsoluteTimeRange, LanguageProvider, HistoryItem } from '@grafana/data'
|
||||
|
||||
import { CloudWatchDatasource } from './datasource';
|
||||
import { TypeaheadInput, TypeaheadOutput, Token } from '@grafana/ui';
|
||||
import { Grammar } from 'prismjs';
|
||||
import Prism, { Grammar } from 'prismjs';
|
||||
|
||||
export type CloudWatchHistoryItem = HistoryItem<CloudWatchQuery>;
|
||||
|
||||
@@ -64,6 +64,18 @@ export class CloudWatchLanguageProvider extends LanguageProvider {
|
||||
return this.startTask;
|
||||
};
|
||||
|
||||
isStatsQuery(query: string): boolean {
|
||||
const grammar = this.getSyntax();
|
||||
const tokens = Prism.tokenize(query, grammar) ?? [];
|
||||
|
||||
return !!tokens.find(
|
||||
token =>
|
||||
typeof token !== 'string' &&
|
||||
token.content.toString().toLowerCase() === 'stats' &&
|
||||
token.type === 'query-command'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return suggestions based on input that can be then plugged into a typeahead dropdown.
|
||||
* Keep this DOM-free for testing
|
||||
@@ -153,7 +165,7 @@ export class CloudWatchLanguageProvider extends LanguageProvider {
|
||||
return fields;
|
||||
};
|
||||
|
||||
private handleKeyword = async (context?: TypeaheadContext): Promise<TypeaheadOutput | null> => {
|
||||
private handleKeyword = async (context?: TypeaheadContext): Promise<TypeaheadOutput> => {
|
||||
const suggs = await this.getFieldCompletionItems(context?.logGroupNames ?? []);
|
||||
const functionSuggestions = [
|
||||
{ prefixMatch: true, label: 'Functions', items: STRING_FUNCTIONS.concat(DATETIME_FUNCTIONS, IP_FUNCTIONS) },
|
||||
@@ -406,5 +418,5 @@ function isInsideFunctionParenthesis(curToken: Token): boolean {
|
||||
|
||||
function isAfterKeyword(keyword: string, token: Token): boolean {
|
||||
const prevToken = prevNonWhitespaceToken(token);
|
||||
return prevToken?.types.includes('keyword') && prevToken?.content.toLowerCase() === 'by';
|
||||
return !!(prevToken?.types.includes('keyword') && prevToken?.content.toLowerCase() === 'by');
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user