forked from aws/aws-cdk
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(logs): add QueryDefinition L2 Construct (aws#18655)
This PR implemented the [[feature request] (aws-cloudwatch): Saved queries](aws#16395). It will let users be able to create CloudWatch Logs Insights QueryDefinition by using L2 construct. ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
- Loading branch information
1 parent
f1ada62
commit ee237ad
Showing
9 changed files
with
434 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,165 @@ | ||
import { Resource } from '@aws-cdk/core'; | ||
import { Construct } from 'constructs'; | ||
import { CfnQueryDefinition } from '.'; | ||
import { ILogGroup } from './log-group'; | ||
|
||
|
||
/** | ||
* Properties for a QueryString | ||
*/ | ||
export interface QueryStringProps { | ||
/** | ||
* Retrieves the specified fields from log events for display. | ||
* | ||
* @default - no fields in QueryString | ||
*/ | ||
readonly fields?: string[]; | ||
|
||
/** | ||
* Extracts data from a log field and creates one or more ephemeral fields that you can process further in the query. | ||
* | ||
* @default - no parse in QueryString | ||
*/ | ||
readonly parse?: string; | ||
|
||
/** | ||
* Filters the results of a query that's based on one or more conditions. | ||
* | ||
* @default - no filter in QueryString | ||
*/ | ||
readonly filter?: string; | ||
|
||
/** | ||
* Uses log field values to calculate aggregate statistics. | ||
* | ||
* @default - no stats in QueryString | ||
*/ | ||
readonly stats?: string; | ||
|
||
/** | ||
* Sorts the retrieved log events. | ||
* | ||
* @default - no sort in QueryString | ||
*/ | ||
readonly sort?: string; | ||
|
||
/** | ||
* Specifies the number of log events returned by the query. | ||
* | ||
* @default - no limit in QueryString | ||
*/ | ||
readonly limit?: Number; | ||
|
||
/** | ||
* Specifies which fields to display in the query results. | ||
* | ||
* @default - no display in QueryString | ||
*/ | ||
readonly display?: string; | ||
} | ||
|
||
interface QueryStringMap { | ||
readonly fields?: string, | ||
readonly parse?: string, | ||
readonly filter?: string, | ||
readonly stats?: string, | ||
readonly sort?: string, | ||
readonly limit?: Number, | ||
readonly display?: string, | ||
} | ||
|
||
/** | ||
* Define a QueryString | ||
*/ | ||
export class QueryString { | ||
private readonly fields?: string[]; | ||
private readonly parse?: string; | ||
private readonly filter?: string; | ||
private readonly stats?: string; | ||
private readonly sort?: string; | ||
private readonly limit?: Number; | ||
private readonly display?: string; | ||
|
||
constructor(props: QueryStringProps = {}) { | ||
this.fields = props.fields; | ||
this.parse = props.parse; | ||
this.filter = props.filter; | ||
this.stats = props.stats; | ||
this.sort = props.sort; | ||
this.limit = props.limit; | ||
this.display = props.display; | ||
} | ||
|
||
/** | ||
* String representation of this QueryString. | ||
*/ | ||
public toString(): string { | ||
return noUndef({ | ||
fields: this.fields !== undefined ? this.fields.join(', ') : this.fields, | ||
parse: this.parse, | ||
filter: this.filter, | ||
stats: this.stats, | ||
sort: this.sort, | ||
limit: this.limit, | ||
display: this.display, | ||
}).join(' | '); | ||
} | ||
} | ||
|
||
function noUndef(x: QueryStringMap): string[] { | ||
const ret: string[] = []; | ||
for (const [key, value] of Object.entries(x)) { | ||
if (value !== undefined) { | ||
ret.push(`${key} ${value}`); | ||
} | ||
} | ||
return ret; | ||
} | ||
|
||
/** | ||
* Properties for a QueryDefinition | ||
*/ | ||
export interface QueryDefinitionProps { | ||
/** | ||
* Name of the query definition. | ||
*/ | ||
readonly queryDefinitionName: string; | ||
|
||
/** | ||
* The query string to use for this query definition. | ||
*/ | ||
readonly queryString: QueryString; | ||
|
||
/** | ||
* Specify certain log groups for the query definition. | ||
* | ||
* @default - no specified log groups | ||
*/ | ||
readonly logGroups?: ILogGroup[]; | ||
} | ||
|
||
/** | ||
* Define a query definition for CloudWatch Logs Insights | ||
*/ | ||
export class QueryDefinition extends Resource { | ||
/** | ||
* The ID of the query definition. | ||
* | ||
* @attribute | ||
*/ | ||
public readonly queryDefinitionId: string; | ||
|
||
constructor(scope: Construct, id: string, props: QueryDefinitionProps) { | ||
super(scope, id, { | ||
physicalName: props.queryDefinitionName, | ||
}); | ||
|
||
const queryDefinition = new CfnQueryDefinition(this, 'Resource', { | ||
name: props.queryDefinitionName, | ||
queryString: props.queryString.toString(), | ||
logGroupNames: typeof props.logGroups === 'undefined' ? [] : props.logGroups.flatMap(logGroup => logGroup.logGroupName), | ||
}); | ||
|
||
this.queryDefinitionId = queryDefinition.attrQueryDefinitionId; | ||
} | ||
} |
29 changes: 29 additions & 0 deletions
29
packages/@aws-cdk/aws-logs/test/integ.save-logs-insights-query-definition.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
import { App, RemovalPolicy, Stack, StackProps } from '@aws-cdk/core'; | ||
import { LogGroup, QueryDefinition, QueryString } from '../lib'; | ||
|
||
class LogsInsightsQueryDefinitionIntegStack extends Stack { | ||
constructor(scope: App, id: string, props?: StackProps) { | ||
super(scope, id, props); | ||
|
||
const logGroup = new LogGroup(this, 'LogGroup', { | ||
removalPolicy: RemovalPolicy.DESTROY, | ||
}); | ||
|
||
new QueryDefinition(this, 'QueryDefinition', { | ||
queryDefinitionName: 'QueryDefinition', | ||
queryString: new QueryString({ | ||
fields: ['@timestamp', '@message'], | ||
parse: '@message "[*] *" as loggingType, loggingMessage', | ||
filter: 'loggingType = "ERROR"', | ||
sort: '@timestamp desc', | ||
limit: 20, | ||
display: 'loggingMessage', | ||
}), | ||
logGroups: [logGroup], | ||
}); | ||
} | ||
} | ||
|
||
const app = new App(); | ||
new LogsInsightsQueryDefinitionIntegStack(app, 'aws-cdk-logs-querydefinition-integ'); | ||
app.synth(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
import { Template } from '@aws-cdk/assertions'; | ||
import { Stack } from '@aws-cdk/core'; | ||
import { LogGroup, QueryDefinition, QueryString } from '../lib'; | ||
|
||
describe('query definition', () => { | ||
test('create a query definition', () => { | ||
// GIVEN | ||
const stack = new Stack(); | ||
|
||
// WHEN | ||
new QueryDefinition(stack, 'QueryDefinition', { | ||
queryDefinitionName: 'MyQuery', | ||
queryString: new QueryString({ | ||
fields: ['@timestamp', '@message'], | ||
sort: '@timestamp desc', | ||
limit: 20, | ||
}), | ||
}); | ||
|
||
// THEN | ||
Template.fromStack(stack).hasResourceProperties('AWS::Logs::QueryDefinition', { | ||
Name: 'MyQuery', | ||
QueryString: 'fields @timestamp, @message | sort @timestamp desc | limit 20', | ||
}); | ||
}); | ||
|
||
test('create a query definition against certain log groups', () => { | ||
// GIVEN | ||
const stack = new Stack(); | ||
|
||
// WHEN | ||
const logGroup = new LogGroup(stack, 'MyLogGroup'); | ||
|
||
new QueryDefinition(stack, 'QueryDefinition', { | ||
queryDefinitionName: 'MyQuery', | ||
queryString: new QueryString({ | ||
fields: ['@timestamp', '@message'], | ||
sort: '@timestamp desc', | ||
limit: 20, | ||
}), | ||
logGroups: [logGroup], | ||
}); | ||
|
||
// THEN | ||
Template.fromStack(stack).hasResourceProperties('AWS::Logs::QueryDefinition', { | ||
Name: 'MyQuery', | ||
QueryString: 'fields @timestamp, @message | sort @timestamp desc | limit 20', | ||
LogGroupNames: [{ Ref: 'MyLogGroup5C0DAD85' }], | ||
}); | ||
}); | ||
|
||
test('create a query definition with all commands', () => { | ||
// GIVEN | ||
const stack = new Stack(); | ||
|
||
// WHEN | ||
const logGroup = new LogGroup(stack, 'MyLogGroup'); | ||
|
||
new QueryDefinition(stack, 'QueryDefinition', { | ||
queryDefinitionName: 'MyQuery', | ||
queryString: new QueryString({ | ||
fields: ['@timestamp', '@message'], | ||
parse: '@message "[*] *" as loggingType, loggingMessage', | ||
filter: 'loggingType = "ERROR"', | ||
sort: '@timestamp desc', | ||
limit: 20, | ||
display: 'loggingMessage', | ||
}), | ||
logGroups: [logGroup], | ||
}); | ||
|
||
// THEN | ||
Template.fromStack(stack).hasResourceProperties('AWS::Logs::QueryDefinition', { | ||
Name: 'MyQuery', | ||
QueryString: 'fields @timestamp, @message | parse @message "[*] *" as loggingType, loggingMessage | filter loggingType = "ERROR" | sort @timestamp desc | limit 20 | display loggingMessage', | ||
}); | ||
}); | ||
}); |
24 changes: 24 additions & 0 deletions
24
...insights-query-definition.integ.snapshot/aws-cdk-logs-querydefinition-integ.template.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
{ | ||
"Resources": { | ||
"LogGroupF5B46931": { | ||
"Type": "AWS::Logs::LogGroup", | ||
"Properties": { | ||
"RetentionInDays": 731 | ||
}, | ||
"UpdateReplacePolicy": "Delete", | ||
"DeletionPolicy": "Delete" | ||
}, | ||
"QueryDefinition4190BC36": { | ||
"Type": "AWS::Logs::QueryDefinition", | ||
"Properties": { | ||
"Name": "QueryDefinition", | ||
"QueryString": "fields @timestamp, @message | parse @message \"[*] *\" as loggingType, loggingMessage | filter loggingType = \"ERROR\" | sort @timestamp desc | limit 20 | display loggingMessage", | ||
"LogGroupNames": [ | ||
{ | ||
"Ref": "LogGroupF5B46931" | ||
} | ||
] | ||
} | ||
} | ||
} | ||
} |
1 change: 1 addition & 0 deletions
1
packages/@aws-cdk/aws-logs/test/save-logs-insights-query-definition.integ.snapshot/cdk.out
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
{"version":"17.0.0"} |
34 changes: 34 additions & 0 deletions
34
...s/@aws-cdk/aws-logs/test/save-logs-insights-query-definition.integ.snapshot/manifest.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
{ | ||
"version": "17.0.0", | ||
"artifacts": { | ||
"Tree": { | ||
"type": "cdk:tree", | ||
"properties": { | ||
"file": "tree.json" | ||
} | ||
}, | ||
"aws-cdk-logs-querydefinition-integ": { | ||
"type": "aws:cloudformation:stack", | ||
"environment": "aws://unknown-account/unknown-region", | ||
"properties": { | ||
"templateFile": "aws-cdk-logs-querydefinition-integ.template.json", | ||
"validateOnSynth": false | ||
}, | ||
"metadata": { | ||
"/aws-cdk-logs-querydefinition-integ/LogGroup/Resource": [ | ||
{ | ||
"type": "aws:cdk:logicalId", | ||
"data": "LogGroupF5B46931" | ||
} | ||
], | ||
"/aws-cdk-logs-querydefinition-integ/QueryDefinition/Resource": [ | ||
{ | ||
"type": "aws:cdk:logicalId", | ||
"data": "QueryDefinition4190BC36" | ||
} | ||
] | ||
}, | ||
"displayName": "aws-cdk-logs-querydefinition-integ" | ||
} | ||
} | ||
} |
Oops, something went wrong.