Skip to content

Commit

Permalink
feat(aws-cloudwatch): log query widget visualisation types (#9694)
Browse files Browse the repository at this point in the history
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*
  • Loading branch information
gianlucb committed Aug 18, 2020
1 parent 2a7cce5 commit 8debcf8
Show file tree
Hide file tree
Showing 6 changed files with 233 additions and 12 deletions.
1 change: 1 addition & 0 deletions packages/@aws-cdk/aws-cloudwatch/README.md
Expand Up @@ -340,6 +340,7 @@ A `LogQueryWidget` shows the results of a query from Logs Insights:
```ts
dashboard.addWidgets(new LogQueryWidget({
logGroupNames: ['my-log-group'],
view: LogQueryVisualizationType.TABLE,
// The lines will be automatically combined using '\n|'.
queryLines: [
'fields @message',
Expand Down
54 changes: 48 additions & 6 deletions packages/@aws-cdk/aws-cloudwatch/lib/log-query.ts
@@ -1,6 +1,32 @@
import * as cdk from '@aws-cdk/core';
import { ConcreteWidget } from './widget';

/**
* Types of view
*/
export enum LogQueryVisualizationType {
/**
* Table view
*/
TABLE = 'table',
/**
* Line view
*/
LINE = 'line',
/**
* Stacked area view
*/
STACKEDAREA = 'stackedarea',
/**
* Bar view
*/
BAR = 'bar',
/**
* Pie view
*/
PIE = 'pie',
}

/**
* Properties for a Query widget
*/
Expand Down Expand Up @@ -43,6 +69,13 @@ export interface LogQueryWidgetProps {
*/
readonly region?: string;

/**
* The type of view to use
*
* @default LogQueryVisualizationType.TABLE
*/
readonly view?: LogQueryVisualizationType;

/**
* Width of the widget, in a grid of 24 units wide
*
Expand Down Expand Up @@ -83,18 +116,27 @@ export class LogQueryWidget extends ConcreteWidget {
? this.props.queryLines.join('\n| ')
: this.props.queryString;

const properties: any = {
view: this.props.view? this.props.view : LogQueryVisualizationType.TABLE,
title: this.props.title,
region: this.props.region || cdk.Aws.REGION,
query: `${sources} | ${query}`,
};

// adding stacked property in case of LINE or STACKEDAREA
if (this.props.view === LogQueryVisualizationType.LINE || this.props.view === LogQueryVisualizationType.STACKEDAREA) {
// assign the right native view value. both types share the same value
properties.view = 'timeSeries',
properties.stacked = this.props.view === LogQueryVisualizationType.STACKEDAREA ? true : false;
}

return [{
type: 'log',
width: this.width,
height: this.height,
x: this.x,
y: this.y,
properties: {
view: 'table',
title: this.props.title,
region: this.props.region || cdk.Aws.REGION,
query: `${sources} | ${query}`,
},
properties: properties,
}];
}
}
Expand Up @@ -71,6 +71,22 @@
{
"Ref": "AWS::Region"
},
"\",\"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\":\"",
{
"Ref": "AWS::Region"
},
"\",\"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\":\"",
{
"Ref": "AWS::Region"
},
"\",\"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\":\"",
{
"Ref": "AWS::Region"
},
"\",\"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\":\"",
{
"Ref": "AWS::Region"
},
"\",\"query\":\"SOURCE 'my-log-group' | fields @message\\n | filter @message like /Error/\"}}]}"
]
]
Expand Down
Expand Up @@ -9,7 +9,7 @@ import * as cloudwatch from '../lib';

const app = new cdk.App();

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

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

Expand Down Expand Up @@ -54,5 +54,33 @@ dashboard.addWidgets(new cloudwatch.LogQueryWidget({
queryString: `fields @message
| filter @message like /Error/`,
}));
dashboard.addWidgets(new cloudwatch.LogQueryWidget({
title: 'Errors in my log group - bar',
view: cloudwatch.LogQueryVisualizationType.BAR,
logGroupNames: ['my-log-group'],
queryString: `fields @message
| filter @message like /Error/`,
}));
dashboard.addWidgets(new cloudwatch.LogQueryWidget({
title: 'Errors in my log group - line',
view: cloudwatch.LogQueryVisualizationType.LINE,
logGroupNames: ['my-log-group'],
queryString: `fields @message
| filter @message like /Error/`,
}));
dashboard.addWidgets(new cloudwatch.LogQueryWidget({
title: 'Errors in my log group - stacked',
view: cloudwatch.LogQueryVisualizationType.STACKEDAREA,
logGroupNames: ['my-log-group'],
queryString: `fields @message
| filter @message like /Error/`,
}));
dashboard.addWidgets(new cloudwatch.LogQueryWidget({
title: 'Errors in my log group - pie',
view: cloudwatch.LogQueryVisualizationType.PIE,
logGroupNames: ['my-log-group'],
queryString: `fields @message
| filter @message like /Error/`,
}));

app.synth();
Expand Up @@ -58,19 +58,31 @@
[
"(((ALARM(",
{
"Fn::GetAtt": [ "Alarm1F9009D71", "Arn" ]
"Fn::GetAtt": [
"Alarm1F9009D71",
"Arn"
]
},
") OR OK(",
{
"Fn::GetAtt": [ "Alarm2A7122E13", "Arn" ]
"Fn::GetAtt": [
"Alarm2A7122E13",
"Arn"
]
},
") OR ALARM(",
{
"Fn::GetAtt": [ "Alarm32341D8D9", "Arn" ]
"Fn::GetAtt": [
"Alarm32341D8D9",
"Arn"
]
},
")) AND (NOT (INSUFFICIENT_DATA(",
{
"Fn::GetAtt":[ "Alarm4671832C8", "Arn" ]
"Fn::GetAtt": [
"Alarm4671832C8",
"Arn"
]
},
")))) OR FALSE)"
]
Expand Down
124 changes: 123 additions & 1 deletion packages/@aws-cdk/aws-cloudwatch/test/test.graphs.ts
@@ -1,6 +1,6 @@
import { Stack } from '@aws-cdk/core';
import { Test } from 'nodeunit';
import { Alarm, AlarmWidget, Color, GraphWidget, LegendPosition, LogQueryWidget, Metric, Shading, SingleValueWidget } from '../lib';
import { Alarm, AlarmWidget, Color, GraphWidget, LegendPosition, LogQueryWidget, Metric, Shading, SingleValueWidget, LogQueryVisualizationType } from '../lib';

export = {
'add stacked property to graphs'(test: Test) {
Expand Down Expand Up @@ -142,6 +142,128 @@ export = {
test.done();
},

'query result widget - bar'(test: Test) {
// GIVEN
const stack = new Stack();
const logGroup = { logGroupName: 'my-log-group' };

// WHEN
const widget = new LogQueryWidget({
logGroupNames: [logGroup.logGroupName],
view: LogQueryVisualizationType.BAR,
queryLines: [
'fields @message',
'filter @message like /Error/',
],
});

// THEN
test.deepEqual(stack.resolve(widget.toJson()), [{
type: 'log',
width: 6,
height: 6,
properties: {
view: 'bar',
region: { Ref: 'AWS::Region' },
query: `SOURCE '${logGroup.logGroupName}' | fields @message\n| filter @message like /Error/`,
},
}]);

test.done();
},

'query result widget - pie'(test: Test) {
// GIVEN
const stack = new Stack();
const logGroup = { logGroupName: 'my-log-group' };

// WHEN
const widget = new LogQueryWidget({
logGroupNames: [logGroup.logGroupName],
view: LogQueryVisualizationType.PIE,
queryLines: [
'fields @message',
'filter @message like /Error/',
],
});

// THEN
test.deepEqual(stack.resolve(widget.toJson()), [{
type: 'log',
width: 6,
height: 6,
properties: {
view: 'pie',
region: { Ref: 'AWS::Region' },
query: `SOURCE '${logGroup.logGroupName}' | fields @message\n| filter @message like /Error/`,
},
}]);

test.done();
},

'query result widget - line'(test: Test) {
// GIVEN
const stack = new Stack();
const logGroup = { logGroupName: 'my-log-group' } ;

// WHEN
const widget = new LogQueryWidget({
logGroupNames: [logGroup.logGroupName],
view: LogQueryVisualizationType.LINE,
queryLines: [
'fields @message',
'filter @message like /Error/',
],
});

// THEN
test.deepEqual(stack.resolve(widget.toJson()), [{
type: 'log',
width: 6,
height: 6,
properties: {
view: 'timeSeries',
stacked: false,
region: { Ref: 'AWS::Region' },
query: `SOURCE '${logGroup.logGroupName}' | fields @message\n| filter @message like /Error/`,
},
}]);

test.done();
},

'query result widget - stackedarea'(test: Test) {
// GIVEN
const stack = new Stack();
const logGroup = { logGroupName: 'my-log-group' };

// WHEN
const widget = new LogQueryWidget({
logGroupNames: [logGroup.logGroupName],
view: LogQueryVisualizationType.STACKEDAREA,
queryLines: [
'fields @message',
'filter @message like /Error/',
],
});

// THEN
test.deepEqual(stack.resolve(widget.toJson()), [{
type: 'log',
width: 6,
height: 6,
properties: {
view: 'timeSeries',
stacked: true,
region: { Ref: 'AWS::Region' },
query: `SOURCE '${logGroup.logGroupName}' | fields @message\n| filter @message like /Error/`,
},
}]);

test.done();
},

'alarm widget'(test: Test) {
// GIVEN
const stack = new Stack();
Expand Down

0 comments on commit 8debcf8

Please sign in to comment.