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', 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 65dfd113e4dbe..414d2677836bc 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();