From 3ef52510baf7b864f45b4c562140547b0fedc50f Mon Sep 17 00:00:00 2001 From: gianlucb Date: Fri, 14 Aug 2020 08:28:34 +0000 Subject: [PATCH 1/4] feat(aws-cloudwatch): support for log query widget visualisation types --- .../@aws-cdk/aws-cloudwatch/lib/log-query.ts | 54 +++++++- .../integ.alarm-and-dashboard.expected.json | 16 +++ .../test/integ.alarm-and-dashboard.ts | 30 ++++- .../test/integ.composite-alarm.expected.json | 20 ++- .../aws-cloudwatch/test/test.graphs.ts | 124 +++++++++++++++++- 5 files changed, 232 insertions(+), 12 deletions(-) diff --git a/packages/@aws-cdk/aws-cloudwatch/lib/log-query.ts b/packages/@aws-cdk/aws-cloudwatch/lib/log-query.ts index 506e6a373bca4..3984fb9bd88f4 100644 --- a/packages/@aws-cdk/aws-cloudwatch/lib/log-query.ts +++ b/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 */ @@ -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 * @@ -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, }]; } } diff --git a/packages/@aws-cdk/aws-cloudwatch/test/integ.alarm-and-dashboard.expected.json b/packages/@aws-cdk/aws-cloudwatch/test/integ.alarm-and-dashboard.expected.json index fa5b2236c9a89..3162bb6dd9e54 100644 --- a/packages/@aws-cdk/aws-cloudwatch/test/integ.alarm-and-dashboard.expected.json +++ b/packages/@aws-cdk/aws-cloudwatch/test/integ.alarm-and-dashboard.expected.json @@ -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/\"}}]}" ] ] diff --git a/packages/@aws-cdk/aws-cloudwatch/test/integ.alarm-and-dashboard.ts b/packages/@aws-cdk/aws-cloudwatch/test/integ.alarm-and-dashboard.ts index ca6a01c2bf6bf..01f8f5be9817a 100644 --- a/packages/@aws-cdk/aws-cloudwatch/test/integ.alarm-and-dashboard.ts +++ b/packages/@aws-cdk/aws-cloudwatch/test/integ.alarm-and-dashboard.ts @@ -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' }); @@ -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(); diff --git a/packages/@aws-cdk/aws-cloudwatch/test/integ.composite-alarm.expected.json b/packages/@aws-cdk/aws-cloudwatch/test/integ.composite-alarm.expected.json index 4febb9e015123..87ae41c2f517e 100644 --- a/packages/@aws-cdk/aws-cloudwatch/test/integ.composite-alarm.expected.json +++ b/packages/@aws-cdk/aws-cloudwatch/test/integ.composite-alarm.expected.json @@ -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)" ] diff --git a/packages/@aws-cdk/aws-cloudwatch/test/test.graphs.ts b/packages/@aws-cdk/aws-cloudwatch/test/test.graphs.ts index e533b0fba304e..b6fe44c7a78a8 100644 --- a/packages/@aws-cdk/aws-cloudwatch/test/test.graphs.ts +++ b/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) { @@ -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(); From 67d972a47113fd29ad058eb00b4ddc5d99ae2fd0 Mon Sep 17 00:00:00 2001 From: gianlucb Date: Fri, 14 Aug 2020 08:57:42 +0000 Subject: [PATCH 2/4] chore(aws-cloudwatch): updated README --- packages/@aws-cdk/aws-cloudwatch/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/@aws-cdk/aws-cloudwatch/README.md b/packages/@aws-cdk/aws-cloudwatch/README.md index f9781b8736f9e..82b9bd250b31b 100644 --- a/packages/@aws-cdk/aws-cloudwatch/README.md +++ b/packages/@aws-cdk/aws-cloudwatch/README.md @@ -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', From eb9f1ffa403ec51a74027fd6be532947cd21c565 Mon Sep 17 00:00:00 2001 From: gianlucb Date: Mon, 17 Aug 2020 17:37:14 +0000 Subject: [PATCH 3/4] chore(aws-cloudwatch): fixed awslint errors --- packages/@aws-cdk/aws-cloudwatch/test/test.graphs.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/@aws-cdk/aws-cloudwatch/test/test.graphs.ts b/packages/@aws-cdk/aws-cloudwatch/test/test.graphs.ts index b6fe44c7a78a8..903389728afa7 100644 --- a/packages/@aws-cdk/aws-cloudwatch/test/test.graphs.ts +++ b/packages/@aws-cdk/aws-cloudwatch/test/test.graphs.ts @@ -145,7 +145,7 @@ export = { 'query result widget - bar'(test: Test) { // GIVEN const stack = new Stack(); - const logGroup = {logGroupName: 'my-log-group'}; + const logGroup = { logGroupName: 'my-log-group' }; // WHEN const widget = new LogQueryWidget({ @@ -175,7 +175,7 @@ export = { 'query result widget - pie'(test: Test) { // GIVEN const stack = new Stack(); - const logGroup = {logGroupName: 'my-log-group'}; + const logGroup = { logGroupName: 'my-log-group' }; // WHEN const widget = new LogQueryWidget({ @@ -205,7 +205,7 @@ export = { 'query result widget - line'(test: Test) { // GIVEN const stack = new Stack(); - const logGroup = {logGroupName: 'my-log-group'}; + const logGroup = { logGroupName: 'my-log-group'} ; // WHEN const widget = new LogQueryWidget({ @@ -236,7 +236,7 @@ export = { 'query result widget - stackedarea'(test: Test) { // GIVEN const stack = new Stack(); - const logGroup = {logGroupName: 'my-log-group'}; + const logGroup = { logGroupName: 'my-log-group' }; // WHEN const widget = new LogQueryWidget({ From 4c26e83e7f5c25a7a10f194ef30ccd42b63be804 Mon Sep 17 00:00:00 2001 From: gianlucb Date: Mon, 17 Aug 2020 21:06:33 +0000 Subject: [PATCH 4/4] chore(aws-cloudwatch): fixed awslint errors --- packages/@aws-cdk/aws-cloudwatch/test/test.graphs.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/@aws-cdk/aws-cloudwatch/test/test.graphs.ts b/packages/@aws-cdk/aws-cloudwatch/test/test.graphs.ts index 325e44ad553e7..414d2677836bc 100644 --- a/packages/@aws-cdk/aws-cloudwatch/test/test.graphs.ts +++ b/packages/@aws-cdk/aws-cloudwatch/test/test.graphs.ts @@ -205,7 +205,7 @@ export = { 'query result widget - line'(test: Test) { // GIVEN const stack = new Stack(); - const logGroup = { logGroupName: 'my-log-group'} ; + const logGroup = { logGroupName: 'my-log-group' } ; // WHEN const widget = new LogQueryWidget({