Skip to content

Commit

Permalink
feat(cloudwatch): LogGroup Query Widget
Browse files Browse the repository at this point in the history
Add a `LogQueryWidget` to generate a dashboard widget
showing the results of a query from Logs Insights.
This was a missing feature which is available in
the console.

closes #3681
  • Loading branch information
OliverGavin committed May 5, 2020
1 parent e72c353 commit 1275952
Show file tree
Hide file tree
Showing 6 changed files with 157 additions and 2 deletions.
15 changes: 15 additions & 0 deletions packages/@aws-cdk/aws-cloudwatch/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,21 @@ dashboard.addWidgets(new TextWidget({
}));
```

### Query results widget

A `LogQueryWidget` shows the results of a query from Logs Insights:

```ts
dashboard.addWidgets(new LogQueryWidget({
logGroupNames: ['my-log-group'],
// The lines will be automatically combined using '\n|'.
queryLines: [
'fields @message',
'filter @message like /Error/',
]
}));
```

### Dashboard Layout

The widgets on a dashboard are visually laid out in a grid that is 24 columns
Expand Down
1 change: 1 addition & 0 deletions packages/@aws-cdk/aws-cloudwatch/lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export * from './graph';
export * from './layout';
export * from './metric';
export * from './metric-types';
export * from './log-query';
export * from './text';
export * from './widget';

Expand Down
100 changes: 100 additions & 0 deletions packages/@aws-cdk/aws-cloudwatch/lib/log-query.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import * as cdk from '@aws-cdk/core';
import { ConcreteWidget } from './widget';

/**
* Properties for a Query widget
*/
export interface LogQueryWidgetProps {
/**
* Title for the widget
*
* @default No title
*/
readonly title?: string;

/**
* Names of log groups to query
*/
readonly logGroupNames: string[];

/**
* Full query string for log insights
*
* Be sure to prepend every new line with a newline and pipe character
* (`\n|`).
*
* @default - Exactly one of `queryString`, `queryLines` is required.
*/
readonly queryString?: string;

/**
* A sequence of lines to use to build the query
*
* The query will be built by joining the lines together using `\n|`.
*
* @default - Exactly one of `queryString`, `queryLines` is required.
*/
readonly queryLines?: string[];

/**
* The region the metrics of this widget should be taken from
*
* @default Current region
*/
readonly region?: string;

/**
* Width of the widget, in a grid of 24 units wide
*
* @default 6
*/
readonly width?: number;

/**
* Height of the widget
*
* @default 6
*/
readonly height?: number;
}

/**
* Display query results from Logs Insights
*/
export class LogQueryWidget extends ConcreteWidget {
private readonly props: LogQueryWidgetProps;

constructor(props: LogQueryWidgetProps) {
super(props.width || 6, props.height || 6);
this.props = props;

if (props.logGroupNames.length === 0) {
throw new Error('Specify at least one log group name.');
}

if (!!props.queryString === !!props.queryLines) {
throw new Error('Specify exactly one of \'queryString\' and \'queryLines\'');
}
}

public toJson(): any[] {
const sources = this.props.logGroupNames.map(l => `SOURCE '${l}'`).join(' | ');
const query = this.props.queryLines
? this.props.queryLines.join('\n| ')
: this.props.queryString;

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}`,
},
}];
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,11 @@
"QueueName"
]
},
"\"]]}}]}"
"\"]]}},{\"type\":\"log\",\"width\":6,\"height\":6,\"x\":0,\"y\":17,\"properties\":{\"view\":\"table\",\"title\":\"Errors in my log group\",\"region\":\"",
{
"Ref": "AWS::Region"
},
"\",\"query\":\"SOURCE 'my-log-group' | fields @message\\n | filter @message like /Error/\"}}]}"
]
]
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,5 +48,11 @@ dashboard.addWidgets(new cloudwatch.SingleValueWidget({
title: 'Current messages in queue',
metrics: [metric],
}));
dashboard.addWidgets(new cloudwatch.LogQueryWidget({
title: 'Errors in my log group',
logGroupNames: ['my-log-group'],
queryString: `fields @message
| filter @message like /Error/`,
}));

app.synth();
31 changes: 30 additions & 1 deletion packages/@aws-cdk/aws-cloudwatch/test/test.graphs.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Stack } from '@aws-cdk/core';
import { Test } from 'nodeunit';
import { Alarm, AlarmWidget, Color, GraphWidget, Metric, Shading, SingleValueWidget } from '../lib';
import { Alarm, AlarmWidget, Color, GraphWidget, LogQueryWidget, Metric, Shading, SingleValueWidget } from '../lib';

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

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

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

// THEN
test.deepEqual(stack.resolve(widget.toJson()), [{
type: 'log',
width: 6,
height: 6,
properties: {
view: 'table',
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 1275952

Please sign in to comment.