Datasource/CloudWatch: Add data links to CloudWatch logs for deep linking to AWS (#24334)

* Datasource/CloudWatch: Fix encoding of CloudWatch Logs deep link URL

* Adds data links to cloudwatch logs responses for deep linking to aws console

* Implements PR feedback
This commit is contained in:
kay delaney 2020-05-08 17:03:15 +01:00 committed by GitHub
parent 35c097e475
commit a655aa1ca8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 96 additions and 5 deletions

View File

@ -12,7 +12,7 @@ export interface AwsUrl {
}
export function encodeUrl(obj: AwsUrl, region: string): string {
return `https://${region}.console.aws.amazon.com/cloudwatch/home?region=${region}#logsV2:logs-insights$3FqueryDetail$3D${JSURL.stringify(
return `https://${region}.console.aws.amazon.com/cloudwatch/home?region=${region}#logs-insights:queryDetail=${JSURL.stringify(
obj
)}`;
}

View File

@ -47,6 +47,7 @@ import { CloudWatchLanguageProvider } from './language_provider';
const TSDB_QUERY_ENDPOINT = '/api/tsdb/query';
import { VariableWithMultiSupport } from 'app/features/templating/types';
import { RowContextOptions } from '@grafana/ui/src/components/Logs/LogRowContextProvider';
import { AwsUrl, encodeUrl } from './aws_url';
const displayAlert = (datasourceName: string, region: string) =>
store.dispatch(
@ -118,7 +119,8 @@ export class CloudWatchDatasource extends DataSourceApi<CloudWatchQuery, CloudWa
refId: dataFrame.refId,
}))
)
)
),
map(response => this.addDataLinksToLogsResponse(response, options))
);
}
@ -224,12 +226,48 @@ export class CloudWatchDatasource extends DataSourceApi<CloudWatchQuery, CloudWa
);
}
private addDataLinksToLogsResponse(response: DataQueryResponse, options: DataQueryRequest<CloudWatchQuery>) {
for (const dataFrame of response.data as DataFrame[]) {
const range = this.timeSrv.timeRange();
const start = range.from.toISOString();
const end = range.to.toISOString();
const curTarget = options.targets.find(target => target.refId === dataFrame.refId) as CloudWatchLogsQuery;
const urlProps: AwsUrl = {
end,
start,
timeType: 'ABSOLUTE',
tz: 'UTC',
editorString: curTarget.expression,
isLiveTail: false,
source: curTarget.logGroupNames,
};
const encodedUrl = encodeUrl(
urlProps,
this.getActualRegion(this.replace(curTarget.region, options.scopedVars, true, 'region'))
);
for (const field of dataFrame.fields) {
field.config.links = [
{
url: encodedUrl,
title: 'View in CloudWatch console',
targetBlank: true,
},
];
}
}
return response;
}
stopQueries() {
if (this.logQueries.size > 0) {
this.makeLogActionRequest(
'StopQuery',
[...this.logQueries.values()].map(logQuery => ({ queryId: logQuery.id, region: logQuery.region })),
null,
undefined,
false
).pipe(finalize(() => this.logQueries.clear()));
}

View File

@ -1,7 +1,7 @@
import '../datasource';
import { CloudWatchDatasource } from '../datasource';
import * as redux from 'app/store/store';
import { DataSourceInstanceSettings, dateMath, getFrameDisplayTitle } from '@grafana/data';
import { DataSourceInstanceSettings, dateMath, getFrameDisplayTitle, DataQueryResponse } from '@grafana/data';
import { TemplateSrv } from 'app/features/templating/template_srv';
import { CustomVariable } from 'app/features/templating/all';
import { CloudWatchQuery, CloudWatchMetricsQuery } from '../types';
@ -28,7 +28,7 @@ describe('CloudWatchDatasource', () => {
const defaultTimeRange = { from: new Date(start), to: new Date(start + 3600 * 1000) };
const timeSrv = {
time: { from: 'now-1h', to: 'now' },
time: { from: '2016-12-31 15:00:00', to: '2016-12-31 16:00:00' },
timeRange: () => {
return {
from: dateMath.parse(timeSrv.time.from, false),
@ -105,6 +105,59 @@ describe('CloudWatchDatasource', () => {
});
});
describe('When performing CloudWatch logs query', () => {
it('should add data links to response', () => {
const mockResponse: DataQueryResponse = {
data: [
{
fields: [
{
config: {
links: [],
},
},
],
refId: 'A',
},
],
};
const mockOptions = {
targets: [
{
refId: 'A',
expression: 'stats count(@message) by bin(1h)',
logGroupNames: ['fake-log-group-one', 'fake-log-group-two'],
region: 'default',
},
],
};
const saturatedResponse = ctx.ds.addDataLinksToLogsResponse(mockResponse, mockOptions);
expect(saturatedResponse).toMatchObject({
data: [
{
fields: [
{
config: {
links: [
{
url:
"https://us-east-1.console.aws.amazon.com/cloudwatch/home?region=us-east-1#logs-insights:queryDetail=~(end~'2016-12-31T16*3a00*3a00.000Z~start~'2016-12-31T15*3a00*3a00.000Z~timeType~'ABSOLUTE~tz~'UTC~editorString~'stats*20count*28*40message*29*20by*20bin*281h*29~isLiveTail~false~source~(~'fake-log-group-one~'fake-log-group-two))",
title: 'View in CloudWatch console',
targetBlank: true,
},
],
},
},
],
refId: 'A',
},
],
});
});
});
describe('When performing CloudWatch metrics query', () => {
const query = {
range: defaultTimeRange,