Skip to content

Commit 8a77828

Browse files
authored
feat(cloudwatch-dashboards): add support for queryLanguage property (#34547)
### Issue # (if applicable) Closes #34482 ### Reason for this change CloudWatch [recently added support for using multiple query languages to query logs](https://aws.amazon.com/blogs/aws/new-amazon-cloudwatch-and-amazon-opensearch-service-launch-an-integrated-analytics-experience/). Currently, CDK is missing the functionality to specify a query language when creating a Log Query Widget inside a CloudWatch Dashboard. ### Description of changes Applied [suggested implementation](#34482 (comment)) by @ykethan and added tests. ### Describe any new or updated permissions being added N/A ### Description of how you validated changes Added unit test and updated integ test. ### Checklist - [x] My code adheres to the [CONTRIBUTING GUIDE](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md) and [DESIGN GUIDELINES](https://github.com/aws/aws-cdk/blob/main/docs/DESIGN_GUIDELINES.md) ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
1 parent aaf442b commit 8a77828

File tree

9 files changed

+613
-12
lines changed

9 files changed

+613
-12
lines changed

packages/@aws-cdk-testing/framework-integ/test/aws-cloudwatch/test/integ.alarm-and-dashboard.js.snapshot/aws-cdk-cloudwatch-alarms.assets.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/@aws-cdk-testing/framework-integ/test/aws-cloudwatch/test/integ.alarm-and-dashboard.js.snapshot/aws-cdk-cloudwatch-alarms.template.json

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,15 @@
9494
{
9595
"Ref": "AWS::Region"
9696
},
97-
"\",\"accountId\":\"123456789012\",\"query\":\"SOURCE 'my-log-group' | fields @message\\n | filter @message like /Error/\"}},{\"type\":\"metric\",\"width\":6,\"height\":3,\"x\":0,\"y\":50,\"properties\":{\"view\":\"singleValue\",\"title\":\"Sent message size\",\"region\":\"",
97+
"\",\"accountId\":\"123456789012\",\"query\":\"SOURCE 'my-log-group' | fields @message\\n | filter @message like /Error/\"}},{\"type\":\"log\",\"width\":6,\"height\":6,\"x\":0,\"y\":50,\"properties\":{\"view\":\"table\",\"title\":\"Errors in my log group - SQL\",\"region\":\"",
98+
{
99+
"Ref": "AWS::Region"
100+
},
101+
"\",\"query\":\"SOURCE 'my-log-group' | SELECT count(*) as count FROM 'my-log-group'\",\"queryLanguage\":\"SQL\"}},{\"type\":\"log\",\"width\":6,\"height\":6,\"x\":0,\"y\":56,\"properties\":{\"view\":\"table\",\"title\":\"Errors in my log group - PPL\",\"region\":\"",
102+
{
103+
"Ref": "AWS::Region"
104+
},
105+
"\",\"query\":\"SOURCE 'my-log-group' | fields `@message` | sort - `@timestamp`\",\"queryLanguage\":\"PPL\"}},{\"type\":\"metric\",\"width\":6,\"height\":3,\"x\":0,\"y\":62,\"properties\":{\"view\":\"singleValue\",\"title\":\"Sent message size\",\"region\":\"",
98106
{
99107
"Ref": "AWS::Region"
100108
},
@@ -105,7 +113,7 @@
105113
"QueueName"
106114
]
107115
},
108-
"\"]],\"singleValueFullPrecision\":false}},{\"type\":\"metric\",\"width\":6,\"height\":3,\"x\":0,\"y\":53,\"properties\":{\"view\":\"singleValue\",\"title\":\"Sent message size with full precision\",\"region\":\"",
116+
"\"]],\"singleValueFullPrecision\":false}},{\"type\":\"metric\",\"width\":6,\"height\":3,\"x\":0,\"y\":65,\"properties\":{\"view\":\"singleValue\",\"title\":\"Sent message size with full precision\",\"region\":\"",
109117
{
110118
"Ref": "AWS::Region"
111119
},
@@ -116,7 +124,7 @@
116124
"QueueName"
117125
]
118126
},
119-
"\"]],\"singleValueFullPrecision\":true}},{\"type\":\"custom\",\"width\":6,\"height\":6,\"x\":0,\"y\":56,\"properties\":{\"endpoint\":\"arn:aws:lambda:us-west-2:123456789012:function:my-function\",\"title\":\"My custom alarm\",\"updateOn\":{\"refresh\":true,\"resize\":true,\"timeRange\":true}}}]}"
127+
"\"]],\"singleValueFullPrecision\":true}},{\"type\":\"custom\",\"width\":6,\"height\":6,\"x\":0,\"y\":68,\"properties\":{\"endpoint\":\"arn:aws:lambda:us-west-2:123456789012:function:my-function\",\"title\":\"My custom alarm\",\"updateOn\":{\"refresh\":true,\"resize\":true,\"timeRange\":true}}}]}"
120128
]
121129
]
122130
},

packages/@aws-cdk-testing/framework-integ/test/aws-cloudwatch/test/integ.alarm-and-dashboard.js.snapshot/integ.json

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/@aws-cdk-testing/framework-integ/test/aws-cloudwatch/test/integ.alarm-and-dashboard.js.snapshot/manifest.json

Lines changed: 492 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/@aws-cdk-testing/framework-integ/test/aws-cloudwatch/test/integ.alarm-and-dashboard.js.snapshot/tree.json

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/@aws-cdk-testing/framework-integ/test/aws-cloudwatch/test/integ.alarm-and-dashboard.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,18 @@ dashboard.addWidgets(new cloudwatch.LogQueryWidget({
9494
| filter @message like /Error/`,
9595
accountId: '123456789012',
9696
}));
97+
dashboard.addWidgets(new cloudwatch.LogQueryWidget({
98+
title: 'Errors in my log group - SQL',
99+
logGroupNames: ['my-log-group'],
100+
queryString: "SELECT count(*) as count FROM 'my-log-group'",
101+
queryLanguage: cloudwatch.LogQueryLanguage.SQL,
102+
}));
103+
dashboard.addWidgets(new cloudwatch.LogQueryWidget({
104+
title: 'Errors in my log group - PPL',
105+
logGroupNames: ['my-log-group'],
106+
queryString: 'fields `@message`\ | sort - `@timestamp`',
107+
queryLanguage: cloudwatch.LogQueryLanguage.PPL,
108+
}));
97109
dashboard.addWidgets(new cloudwatch.SingleValueWidget({
98110
title: 'Sent message size',
99111
metrics: [sentMessageSizeMetric],

packages/aws-cdk-lib/aws-cloudwatch/README.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -848,6 +848,19 @@ dashboard.addWidgets(new cloudwatch.LogQueryWidget({
848848
}));
849849
```
850850

851+
Log Insights QL is the default query language. You may specify an [alternate query language: OpenSearch PPL or SQL](https://aws.amazon.com/blogs/aws/new-amazon-cloudwatch-and-amazon-opensearch-service-launch-an-integrated-analytics-experience/), if desired:
852+
853+
```ts
854+
declare const dashboard: cloudwatch.Dashboard;
855+
856+
dashboard.addWidgets(new cloudwatch.LogQueryWidget({
857+
logGroupNames: ['my-log-group'],
858+
view: cloudwatch.LogQueryVisualizationType.TABLE,
859+
queryString: "SELECT count(*) as count FROM 'my-log-group'",
860+
queryLanguage: cloudwatch.LogQueryLanguage.SQL,
861+
}));
862+
```
863+
851864
### Custom widget
852865

853866
A `CustomWidget` shows the result of an AWS Lambda function:

packages/aws-cdk-lib/aws-cloudwatch/lib/log-query.ts

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,17 @@ export enum LogQueryVisualizationType {
2626
*/
2727
PIE = 'pie',
2828
}
29+
/**
30+
* Logs Query Language
31+
*/
32+
export enum LogQueryLanguage {
33+
/** Logs Insights QL */
34+
LOGS_INSIGHTS = 'Logs',
35+
/** OpenSearch SQL */
36+
SQL = 'SQL',
37+
/** OpenSearch PPL */
38+
PPL = 'PPL',
39+
}
2940

3041
/**
3142
* Properties for a Query widget
@@ -62,6 +73,12 @@ export interface LogQueryWidgetProps {
6273
*/
6374
readonly queryLines?: string[];
6475

76+
/**
77+
* The query language to use for the query.
78+
* @default LogQueryLanguage.LOGS_INSIGHTS
79+
*/
80+
readonly queryLanguage?: LogQueryLanguage;
81+
6582
/**
6683
* The region the metrics of this widget should be taken from
6784
*
@@ -124,22 +141,26 @@ export class LogQueryWidget extends ConcreteWidget {
124141
throw new cdk.UnscopedValidationError('Specify exactly one of \'queryString\' and \'queryLines\'');
125142
}
126143
}
127-
128144
public toJson(): any[] {
129145
const sources = this.props.logGroupNames.map(l => `SOURCE '${l}'`).join(' | ');
130146
const query = this.props.queryLines
131147
? this.props.queryLines.join('\n| ')
132148
: this.props.queryString;
133149

134150
const properties: any = {
135-
view: this.props.view? this.props.view : LogQueryVisualizationType.TABLE,
151+
view: this.props.view ? this.props.view : LogQueryVisualizationType.TABLE,
136152
title: this.props.title,
137153
region: this.props.region || cdk.Aws.REGION,
138154
accountId: this.props.accountId,
139155
query: `${sources} | ${query}`,
140156
};
141157

142-
// adding stacked property in case of LINE or STACKEDAREA
158+
// Add queryLanguage property if specified
159+
if (this.props.queryLanguage) {
160+
properties.queryLanguage = this.props.queryLanguage;
161+
}
162+
163+
// Add stacked property in case of LINE or STACKEDAREA
143164
if (this.props.view === LogQueryVisualizationType.LINE || this.props.view === LogQueryVisualizationType.STACKEDAREA) {
144165
// assign the right native view value. both types share the same value
145166
properties.view = 'timeSeries',

packages/aws-cdk-lib/aws-cloudwatch/test/graphs.test.ts

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
GraphWidget,
99
GraphWidgetView,
1010
LegendPosition,
11+
LogQueryLanguage,
1112
LogQueryVisualizationType,
1213
LogQueryWidget,
1314
Metric,
@@ -319,6 +320,62 @@ describe('Graphs', () => {
319320
}]);
320321
});
321322

323+
test('query result widget - sql', () => {
324+
// GIVEN
325+
const stack = new Stack();
326+
const logGroup = { logGroupName: 'my-log-group' };
327+
328+
// WHEN
329+
const widget = new LogQueryWidget({
330+
logGroupNames: [logGroup.logGroupName],
331+
view: LogQueryVisualizationType.STACKEDAREA,
332+
queryString: "SELECT count(*) as count FROM 'my-log-group'",
333+
queryLanguage: LogQueryLanguage.SQL,
334+
});
335+
336+
// THEN
337+
expect(stack.resolve(widget.toJson())).toEqual([{
338+
type: 'log',
339+
width: 6,
340+
height: 6,
341+
properties: {
342+
view: 'timeSeries',
343+
stacked: true,
344+
region: { Ref: 'AWS::Region' },
345+
query: "SOURCE 'my-log-group' | SELECT count(*) as count FROM 'my-log-group'",
346+
queryLanguage: 'SQL',
347+
},
348+
}]);
349+
});
350+
351+
test('query result widget - ppl', () => {
352+
// GIVEN
353+
const stack = new Stack();
354+
const logGroup = { logGroupName: 'my-log-group' };
355+
356+
// WHEN
357+
const widget = new LogQueryWidget({
358+
logGroupNames: [logGroup.logGroupName],
359+
view: LogQueryVisualizationType.STACKEDAREA,
360+
queryString: 'fields `@message`\ | sort - `@timestamp`',
361+
queryLanguage: LogQueryLanguage.PPL,
362+
});
363+
364+
// THEN
365+
expect(stack.resolve(widget.toJson())).toEqual([{
366+
type: 'log',
367+
width: 6,
368+
height: 6,
369+
properties: {
370+
view: 'timeSeries',
371+
stacked: true,
372+
region: { Ref: 'AWS::Region' },
373+
query: "SOURCE 'my-log-group' | fields `@message`\ | sort - `@timestamp`",
374+
queryLanguage: 'PPL',
375+
},
376+
}]);
377+
});
378+
322379
test('alarm widget', () => {
323380
// GIVEN
324381
const stack = new Stack();

0 commit comments

Comments
 (0)