Skip to content

Commit 8debcf8

Browse files
authored
feat(aws-cloudwatch): log query widget visualisation types (#9694)
Resolves #9675 ### Testing - added unit tests for each visualization type: **line, pie, bar, stackedarea** - executed integration tests - manually verified in cloudwatch dashboard ### Fix - renamed the stack name in _integ.alarm-and-dashboard.ts_ as was a duplicate of _integ.math-alarm-and-dashboard.ts_ ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
1 parent 2a7cce5 commit 8debcf8

6 files changed

+233
-12
lines changed

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

+1
Original file line numberDiff line numberDiff line change
@@ -340,6 +340,7 @@ A `LogQueryWidget` shows the results of a query from Logs Insights:
340340
```ts
341341
dashboard.addWidgets(new LogQueryWidget({
342342
logGroupNames: ['my-log-group'],
343+
view: LogQueryVisualizationType.TABLE,
343344
// The lines will be automatically combined using '\n|'.
344345
queryLines: [
345346
'fields @message',

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

+48-6
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,32 @@
11
import * as cdk from '@aws-cdk/core';
22
import { ConcreteWidget } from './widget';
33

4+
/**
5+
* Types of view
6+
*/
7+
export enum LogQueryVisualizationType {
8+
/**
9+
* Table view
10+
*/
11+
TABLE = 'table',
12+
/**
13+
* Line view
14+
*/
15+
LINE = 'line',
16+
/**
17+
* Stacked area view
18+
*/
19+
STACKEDAREA = 'stackedarea',
20+
/**
21+
* Bar view
22+
*/
23+
BAR = 'bar',
24+
/**
25+
* Pie view
26+
*/
27+
PIE = 'pie',
28+
}
29+
430
/**
531
* Properties for a Query widget
632
*/
@@ -43,6 +69,13 @@ export interface LogQueryWidgetProps {
4369
*/
4470
readonly region?: string;
4571

72+
/**
73+
* The type of view to use
74+
*
75+
* @default LogQueryVisualizationType.TABLE
76+
*/
77+
readonly view?: LogQueryVisualizationType;
78+
4679
/**
4780
* Width of the widget, in a grid of 24 units wide
4881
*
@@ -83,18 +116,27 @@ export class LogQueryWidget extends ConcreteWidget {
83116
? this.props.queryLines.join('\n| ')
84117
: this.props.queryString;
85118

119+
const properties: any = {
120+
view: this.props.view? this.props.view : LogQueryVisualizationType.TABLE,
121+
title: this.props.title,
122+
region: this.props.region || cdk.Aws.REGION,
123+
query: `${sources} | ${query}`,
124+
};
125+
126+
// adding stacked property in case of LINE or STACKEDAREA
127+
if (this.props.view === LogQueryVisualizationType.LINE || this.props.view === LogQueryVisualizationType.STACKEDAREA) {
128+
// assign the right native view value. both types share the same value
129+
properties.view = 'timeSeries',
130+
properties.stacked = this.props.view === LogQueryVisualizationType.STACKEDAREA ? true : false;
131+
}
132+
86133
return [{
87134
type: 'log',
88135
width: this.width,
89136
height: this.height,
90137
x: this.x,
91138
y: this.y,
92-
properties: {
93-
view: 'table',
94-
title: this.props.title,
95-
region: this.props.region || cdk.Aws.REGION,
96-
query: `${sources} | ${query}`,
97-
},
139+
properties: properties,
98140
}];
99141
}
100142
}

packages/@aws-cdk/aws-cloudwatch/test/integ.alarm-and-dashboard.expected.json

+16
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,22 @@
7171
{
7272
"Ref": "AWS::Region"
7373
},
74+
"\",\"query\":\"SOURCE 'my-log-group' | fields @message\\n | filter @message like /Error/\"}},{\"type\":\"log\",\"width\":6,\"height\":6,\"x\":0,\"y\":23,\"properties\":{\"view\":\"bar\",\"title\":\"Errors in my log group - bar\",\"region\":\"",
75+
{
76+
"Ref": "AWS::Region"
77+
},
78+
"\",\"query\":\"SOURCE 'my-log-group' | fields @message\\n | filter @message like /Error/\"}},{\"type\":\"log\",\"width\":6,\"height\":6,\"x\":0,\"y\":29,\"properties\":{\"view\":\"timeSeries\",\"title\":\"Errors in my log group - line\",\"region\":\"",
79+
{
80+
"Ref": "AWS::Region"
81+
},
82+
"\",\"query\":\"SOURCE 'my-log-group' | fields @message\\n | filter @message like /Error/\",\"stacked\":false}},{\"type\":\"log\",\"width\":6,\"height\":6,\"x\":0,\"y\":35,\"properties\":{\"view\":\"timeSeries\",\"title\":\"Errors in my log group - stacked\",\"region\":\"",
83+
{
84+
"Ref": "AWS::Region"
85+
},
86+
"\",\"query\":\"SOURCE 'my-log-group' | fields @message\\n | filter @message like /Error/\",\"stacked\":true}},{\"type\":\"log\",\"width\":6,\"height\":6,\"x\":0,\"y\":41,\"properties\":{\"view\":\"pie\",\"title\":\"Errors in my log group - pie\",\"region\":\"",
87+
{
88+
"Ref": "AWS::Region"
89+
},
7490
"\",\"query\":\"SOURCE 'my-log-group' | fields @message\\n | filter @message like /Error/\"}}]}"
7591
]
7692
]

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

+29-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import * as cloudwatch from '../lib';
99

1010
const app = new cdk.App();
1111

12-
const stack = new cdk.Stack(app, 'aws-cdk-cloudwatch');
12+
const stack = new cdk.Stack(app, 'aws-cdk-cloudwatch-alarms');
1313

1414
const queue = new cdk.CfnResource(stack, 'queue', { type: 'AWS::SQS::Queue' });
1515

@@ -54,5 +54,33 @@ dashboard.addWidgets(new cloudwatch.LogQueryWidget({
5454
queryString: `fields @message
5555
| filter @message like /Error/`,
5656
}));
57+
dashboard.addWidgets(new cloudwatch.LogQueryWidget({
58+
title: 'Errors in my log group - bar',
59+
view: cloudwatch.LogQueryVisualizationType.BAR,
60+
logGroupNames: ['my-log-group'],
61+
queryString: `fields @message
62+
| filter @message like /Error/`,
63+
}));
64+
dashboard.addWidgets(new cloudwatch.LogQueryWidget({
65+
title: 'Errors in my log group - line',
66+
view: cloudwatch.LogQueryVisualizationType.LINE,
67+
logGroupNames: ['my-log-group'],
68+
queryString: `fields @message
69+
| filter @message like /Error/`,
70+
}));
71+
dashboard.addWidgets(new cloudwatch.LogQueryWidget({
72+
title: 'Errors in my log group - stacked',
73+
view: cloudwatch.LogQueryVisualizationType.STACKEDAREA,
74+
logGroupNames: ['my-log-group'],
75+
queryString: `fields @message
76+
| filter @message like /Error/`,
77+
}));
78+
dashboard.addWidgets(new cloudwatch.LogQueryWidget({
79+
title: 'Errors in my log group - pie',
80+
view: cloudwatch.LogQueryVisualizationType.PIE,
81+
logGroupNames: ['my-log-group'],
82+
queryString: `fields @message
83+
| filter @message like /Error/`,
84+
}));
5785

5886
app.synth();

packages/@aws-cdk/aws-cloudwatch/test/integ.composite-alarm.expected.json

+16-4
Original file line numberDiff line numberDiff line change
@@ -58,19 +58,31 @@
5858
[
5959
"(((ALARM(",
6060
{
61-
"Fn::GetAtt": [ "Alarm1F9009D71", "Arn" ]
61+
"Fn::GetAtt": [
62+
"Alarm1F9009D71",
63+
"Arn"
64+
]
6265
},
6366
") OR OK(",
6467
{
65-
"Fn::GetAtt": [ "Alarm2A7122E13", "Arn" ]
68+
"Fn::GetAtt": [
69+
"Alarm2A7122E13",
70+
"Arn"
71+
]
6672
},
6773
") OR ALARM(",
6874
{
69-
"Fn::GetAtt": [ "Alarm32341D8D9", "Arn" ]
75+
"Fn::GetAtt": [
76+
"Alarm32341D8D9",
77+
"Arn"
78+
]
7079
},
7180
")) AND (NOT (INSUFFICIENT_DATA(",
7281
{
73-
"Fn::GetAtt":[ "Alarm4671832C8", "Arn" ]
82+
"Fn::GetAtt": [
83+
"Alarm4671832C8",
84+
"Arn"
85+
]
7486
},
7587
")))) OR FALSE)"
7688
]

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

+123-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { Stack } from '@aws-cdk/core';
22
import { Test } from 'nodeunit';
3-
import { Alarm, AlarmWidget, Color, GraphWidget, LegendPosition, LogQueryWidget, Metric, Shading, SingleValueWidget } from '../lib';
3+
import { Alarm, AlarmWidget, Color, GraphWidget, LegendPosition, LogQueryWidget, Metric, Shading, SingleValueWidget, LogQueryVisualizationType } from '../lib';
44

55
export = {
66
'add stacked property to graphs'(test: Test) {
@@ -142,6 +142,128 @@ export = {
142142
test.done();
143143
},
144144

145+
'query result widget - bar'(test: Test) {
146+
// GIVEN
147+
const stack = new Stack();
148+
const logGroup = { logGroupName: 'my-log-group' };
149+
150+
// WHEN
151+
const widget = new LogQueryWidget({
152+
logGroupNames: [logGroup.logGroupName],
153+
view: LogQueryVisualizationType.BAR,
154+
queryLines: [
155+
'fields @message',
156+
'filter @message like /Error/',
157+
],
158+
});
159+
160+
// THEN
161+
test.deepEqual(stack.resolve(widget.toJson()), [{
162+
type: 'log',
163+
width: 6,
164+
height: 6,
165+
properties: {
166+
view: 'bar',
167+
region: { Ref: 'AWS::Region' },
168+
query: `SOURCE '${logGroup.logGroupName}' | fields @message\n| filter @message like /Error/`,
169+
},
170+
}]);
171+
172+
test.done();
173+
},
174+
175+
'query result widget - pie'(test: Test) {
176+
// GIVEN
177+
const stack = new Stack();
178+
const logGroup = { logGroupName: 'my-log-group' };
179+
180+
// WHEN
181+
const widget = new LogQueryWidget({
182+
logGroupNames: [logGroup.logGroupName],
183+
view: LogQueryVisualizationType.PIE,
184+
queryLines: [
185+
'fields @message',
186+
'filter @message like /Error/',
187+
],
188+
});
189+
190+
// THEN
191+
test.deepEqual(stack.resolve(widget.toJson()), [{
192+
type: 'log',
193+
width: 6,
194+
height: 6,
195+
properties: {
196+
view: 'pie',
197+
region: { Ref: 'AWS::Region' },
198+
query: `SOURCE '${logGroup.logGroupName}' | fields @message\n| filter @message like /Error/`,
199+
},
200+
}]);
201+
202+
test.done();
203+
},
204+
205+
'query result widget - line'(test: Test) {
206+
// GIVEN
207+
const stack = new Stack();
208+
const logGroup = { logGroupName: 'my-log-group' } ;
209+
210+
// WHEN
211+
const widget = new LogQueryWidget({
212+
logGroupNames: [logGroup.logGroupName],
213+
view: LogQueryVisualizationType.LINE,
214+
queryLines: [
215+
'fields @message',
216+
'filter @message like /Error/',
217+
],
218+
});
219+
220+
// THEN
221+
test.deepEqual(stack.resolve(widget.toJson()), [{
222+
type: 'log',
223+
width: 6,
224+
height: 6,
225+
properties: {
226+
view: 'timeSeries',
227+
stacked: false,
228+
region: { Ref: 'AWS::Region' },
229+
query: `SOURCE '${logGroup.logGroupName}' | fields @message\n| filter @message like /Error/`,
230+
},
231+
}]);
232+
233+
test.done();
234+
},
235+
236+
'query result widget - stackedarea'(test: Test) {
237+
// GIVEN
238+
const stack = new Stack();
239+
const logGroup = { logGroupName: 'my-log-group' };
240+
241+
// WHEN
242+
const widget = new LogQueryWidget({
243+
logGroupNames: [logGroup.logGroupName],
244+
view: LogQueryVisualizationType.STACKEDAREA,
245+
queryLines: [
246+
'fields @message',
247+
'filter @message like /Error/',
248+
],
249+
});
250+
251+
// THEN
252+
test.deepEqual(stack.resolve(widget.toJson()), [{
253+
type: 'log',
254+
width: 6,
255+
height: 6,
256+
properties: {
257+
view: 'timeSeries',
258+
stacked: true,
259+
region: { Ref: 'AWS::Region' },
260+
query: `SOURCE '${logGroup.logGroupName}' | fields @message\n| filter @message like /Error/`,
261+
},
262+
}]);
263+
264+
test.done();
265+
},
266+
145267
'alarm widget'(test: Test) {
146268
// GIVEN
147269
const stack = new Stack();

0 commit comments

Comments
 (0)