From 5bd97d830236c8446c46678bb44c48d658ae7473 Mon Sep 17 00:00:00 2001 From: Mike White Date: Mon, 6 Feb 2023 02:59:01 +0200 Subject: [PATCH 1/5] Add support for multiple parse and filter statements when creating a QueryString --- .../@aws-cdk/aws-logs/lib/query-definition.ts | 77 +++++++++++-------- .../aws-logs/test/query-definition.test.ts | 29 +++++++ 2 files changed, 74 insertions(+), 32 deletions(-) diff --git a/packages/@aws-cdk/aws-logs/lib/query-definition.ts b/packages/@aws-cdk/aws-logs/lib/query-definition.ts index 2dff1ad3d93e1..4b5345d15179d 100644 --- a/packages/@aws-cdk/aws-logs/lib/query-definition.ts +++ b/packages/@aws-cdk/aws-logs/lib/query-definition.ts @@ -16,18 +16,21 @@ export interface QueryStringProps { readonly fields?: string[]; /** - * Extracts data from a log field and creates one or more ephemeral fields that you can process further in the query. + * One or more statements for parsing data from a log field and creating ephemeral fields that can + * be processed further in the query. Each statement provided generates a separate parse line in + * the query string. * * @default - no parse in QueryString */ - readonly parse?: string; + readonly parse?: string | string[]; /** - * Filters the results of a query that's based on one or more conditions. + * One or more statements for filtering the results of a query based on a boolean expression. Each + * statement provided generates a separate filter line in the query string. * * @default - no filter in QueryString */ - readonly filter?: string; + readonly filter?: string | string[]; /** * Uses log field values to calculate aggregate statistics. @@ -58,23 +61,13 @@ export interface QueryStringProps { 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 parse?: string | string[]; + private readonly filter?: string | string[]; private readonly stats?: string; private readonly sort?: string; private readonly limit?: Number; @@ -94,26 +87,46 @@ export class QueryString { * 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('\n| '); + return [ + ...this.buildQueryLines('fields', this.fields?.join(', ')), + ...this.buildQueryLines('parse', this.parse), + ...this.buildQueryLines('filter', this.filter), + ...this.buildQueryLines('stats', this.stats), + ...this.buildQueryLines('sort', this.sort), + ...this.buildQueryLines('limit', this.limit?.toString()), + ...this.buildQueryLines('display', this.display), + ].filter( + (queryLine) => queryLine !== undefined && queryLine.length > 0, + ).join('\n| '); } -} -function noUndef(x: QueryStringMap): string[] { - const ret: string[] = []; - for (const [key, value] of Object.entries(x)) { - if (value !== undefined) { - ret.push(`${key} ${value}`); + /** + * Build an array of query lines given a command and statement(s). + * + * @param command a query command + * @param statements one or more query statements for the specified command, or undefined + * @returns an array of the query string lines generated from the provided command and statements + */ + buildQueryLines(command: string, statements: string[] | string | undefined): string[] { + if (statements === undefined) { + return []; } + + return (typeof statements === 'string' ? [statements] : statements).map( + (statement: string | undefined): string => this.buildQueryLine(command, statement!), + ); + } + + /** + * Build a single query line given a command and statement. + * + * @param command a query command + * @param statement a single query statement + * @returns a single query string line generated from the provided command and statement + */ + buildQueryLine(command: string, statement: string): string { + return statement ? `${command} ${statement}` : ''; } - return ret; } /** diff --git a/packages/@aws-cdk/aws-logs/test/query-definition.test.ts b/packages/@aws-cdk/aws-logs/test/query-definition.test.ts index 7b9fd5cb7d4ff..a611f0c372665 100644 --- a/packages/@aws-cdk/aws-logs/test/query-definition.test.ts +++ b/packages/@aws-cdk/aws-logs/test/query-definition.test.ts @@ -75,4 +75,33 @@ describe('query definition', () => { QueryString: 'fields @timestamp, @message\n| parse @message "[*] *" as loggingType, loggingMessage\n| filter loggingType = "ERROR"\n| sort @timestamp desc\n| limit 20\n| display loggingMessage', }); }); + + test('create a query definition with multiple statements for supported commands', () => { + // GIVEN + const stack = new Stack(); + + // WHEN + new QueryDefinition(stack, 'QueryDefinition', { + queryDefinitionName: 'MyQuery', + queryString: new QueryString({ + fields: ['@timestamp', '@message'], + parse: [ + '@message "[*] *" as loggingType, loggingMessage', + '@message "<*>: *" as differentLoggingType, differentLoggingMessage', + ], + filter: [ + 'loggingType = "ERROR"', + 'loggingMessage = "A very strange error occurred!"', + ], + sort: '@timestamp desc', + limit: 20, + }), + }); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::Logs::QueryDefinition', { + Name: 'MyQuery', + QueryString: 'fields @timestamp, @message\n| parse @message "[*] *" as loggingType, loggingMessage\n| parse @message "<*>: *" as differentLoggingType, differentLoggingMessage\n| filter loggingType = "ERROR"\n| filter loggingMessage = "A very strange error occurred!"\n| sort @timestamp desc\n| limit 20', + }); + }); }); From 3654394b4117a80e52232eeac2322707c65aec50 Mon Sep 17 00:00:00 2001 From: Mike White Date: Mon, 6 Feb 2023 03:38:13 +0200 Subject: [PATCH 2/5] Update README and integ test to cover the new behavior. --- packages/@aws-cdk/aws-logs/README.md | 23 +++++++++++++++++++ ...teg.save-logs-insights-query-definition.ts | 21 +++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/packages/@aws-cdk/aws-logs/README.md b/packages/@aws-cdk/aws-logs/README.md index 784ab7e91760c..98b1a40ffcc49 100644 --- a/packages/@aws-cdk/aws-logs/README.md +++ b/packages/@aws-cdk/aws-logs/README.md @@ -330,12 +330,35 @@ new logs.QueryDefinition(this, 'QueryDefinition', { queryDefinitionName: 'MyQuery', queryString: new logs.QueryString({ fields: ['@timestamp', '@message'], + parse: '@message "[*] *" as loggingType, loggingMessage', + filter: 'loggingType = "ERROR"', sort: '@timestamp desc', limit: 20, }), }); ``` +Query strings can also be constructed with multiple parse and filter statements if these are provided as an array. + +Example: + +```ts +new logs.QueryDefinition(this, 'QueryDefinition', { + queryDefinitionName: 'MyQuery', + queryString: new logs.QueryString({ + fields: ['@timestamp', '@message'], + parse: [ + '@message "[*] *" as loggingType, loggingMessage', + '@message "<*>: *" as differentLoggingType, differentLoggingMessage', + ], + filter: [ + 'loggingType = "ERROR"', + 'loggingMessage = "A very strange error occurred!"', + ] + }), +}); +``` + ## Notes Be aware that Log Group ARNs will always have the string `:*` appended to diff --git a/packages/@aws-cdk/aws-logs/test/integ.save-logs-insights-query-definition.ts b/packages/@aws-cdk/aws-logs/test/integ.save-logs-insights-query-definition.ts index b3f19a5940d11..8be1ad7f0e970 100644 --- a/packages/@aws-cdk/aws-logs/test/integ.save-logs-insights-query-definition.ts +++ b/packages/@aws-cdk/aws-logs/test/integ.save-logs-insights-query-definition.ts @@ -11,6 +11,7 @@ class LogsInsightsQueryDefinitionIntegStack extends Stack { removalPolicy: RemovalPolicy.DESTROY, }); + // Test query creation with single parse and filter statements new QueryDefinition(this, 'QueryDefinition', { queryDefinitionName: 'QueryDefinition', queryString: new QueryString({ @@ -23,6 +24,26 @@ class LogsInsightsQueryDefinitionIntegStack extends Stack { }), logGroups: [logGroup], }); + + // Test query creation with multiple parse and filter statements + new QueryDefinition(this, 'QueryDefinitionWithMultipleStatements', { + queryDefinitionName: 'QueryDefinitionWithMultipleStatements', + queryString: new QueryString({ + fields: ['@timestamp', '@message'], + parse: [ + '@message "[*] *" as loggingType, loggingMessage', + '@message "<*>: *" as differentLoggingType, differentLoggingMessage', + ], + filter: [ + 'loggingType = "ERROR"', + 'loggingMessage = "A very strange error occurred!"', + ], + sort: '@timestamp desc', + limit: 20, + display: 'loggingMessage', + }), + logGroups: [logGroup], + }); } } From 581041c105517f5a724c7ff6e90248c654b072d7 Mon Sep 17 00:00:00 2001 From: Mike White Date: Mon, 6 Feb 2023 14:57:03 +0000 Subject: [PATCH 3/5] Run integ test. --- ...efaultTestDeployAssert902BAAD5.assets.json | 2 +- ...insights-querydefinition-integ.assets.json | 6 ++-- ...sights-querydefinition-integ.template.json | 12 +++++++ .../cdk.out | 2 +- .../integ.json | 2 +- .../manifest.json | 10 ++++-- .../tree.json | 34 +++++++++++++++++-- 7 files changed, 58 insertions(+), 10 deletions(-) diff --git a/packages/@aws-cdk/aws-logs/test/integ.save-logs-insights-query-definition.js.snapshot/LogsInsightsQueryDefinitionIntegTestDefaultTestDeployAssert902BAAD5.assets.json b/packages/@aws-cdk/aws-logs/test/integ.save-logs-insights-query-definition.js.snapshot/LogsInsightsQueryDefinitionIntegTestDefaultTestDeployAssert902BAAD5.assets.json index faca19c9ec2b8..7e7b72e0995a4 100644 --- a/packages/@aws-cdk/aws-logs/test/integ.save-logs-insights-query-definition.js.snapshot/LogsInsightsQueryDefinitionIntegTestDefaultTestDeployAssert902BAAD5.assets.json +++ b/packages/@aws-cdk/aws-logs/test/integ.save-logs-insights-query-definition.js.snapshot/LogsInsightsQueryDefinitionIntegTestDefaultTestDeployAssert902BAAD5.assets.json @@ -1,5 +1,5 @@ { - "version": "21.0.0", + "version": "29.0.0", "files": { "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22": { "source": { diff --git a/packages/@aws-cdk/aws-logs/test/integ.save-logs-insights-query-definition.js.snapshot/aws-cdk-logs-insights-querydefinition-integ.assets.json b/packages/@aws-cdk/aws-logs/test/integ.save-logs-insights-query-definition.js.snapshot/aws-cdk-logs-insights-querydefinition-integ.assets.json index 7388475903110..98998264c32c0 100644 --- a/packages/@aws-cdk/aws-logs/test/integ.save-logs-insights-query-definition.js.snapshot/aws-cdk-logs-insights-querydefinition-integ.assets.json +++ b/packages/@aws-cdk/aws-logs/test/integ.save-logs-insights-query-definition.js.snapshot/aws-cdk-logs-insights-querydefinition-integ.assets.json @@ -1,7 +1,7 @@ { - "version": "21.0.0", + "version": "29.0.0", "files": { - "e6a0a51961925fbc37d9b81431449c256ed453f98089eb70f83850f237b4d722": { + "3546c78647ea20567832041c960a034787f9bfc0128226ea8fbc0894366a4dd0": { "source": { "path": "aws-cdk-logs-insights-querydefinition-integ.template.json", "packaging": "file" @@ -9,7 +9,7 @@ "destinations": { "current_account-current_region": { "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", - "objectKey": "e6a0a51961925fbc37d9b81431449c256ed453f98089eb70f83850f237b4d722.json", + "objectKey": "3546c78647ea20567832041c960a034787f9bfc0128226ea8fbc0894366a4dd0.json", "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" } } diff --git a/packages/@aws-cdk/aws-logs/test/integ.save-logs-insights-query-definition.js.snapshot/aws-cdk-logs-insights-querydefinition-integ.template.json b/packages/@aws-cdk/aws-logs/test/integ.save-logs-insights-query-definition.js.snapshot/aws-cdk-logs-insights-querydefinition-integ.template.json index d4df6bf7837ca..16808c0ca7ef0 100644 --- a/packages/@aws-cdk/aws-logs/test/integ.save-logs-insights-query-definition.js.snapshot/aws-cdk-logs-insights-querydefinition-integ.template.json +++ b/packages/@aws-cdk/aws-logs/test/integ.save-logs-insights-query-definition.js.snapshot/aws-cdk-logs-insights-querydefinition-integ.template.json @@ -19,6 +19,18 @@ } ] } + }, + "QueryDefinitionWithMultipleStatements58A3EF74": { + "Type": "AWS::Logs::QueryDefinition", + "Properties": { + "Name": "QueryDefinitionWithMultipleStatements", + "QueryString": "fields @timestamp, @message\n| parse @message \"[*] *\" as loggingType, loggingMessage\n| parse @message \"<*>: *\" as differentLoggingType, differentLoggingMessage\n| filter loggingType = \"ERROR\"\n| filter loggingMessage = \"A very strange error occurred!\"\n| sort @timestamp desc\n| limit 20\n| display loggingMessage", + "LogGroupNames": [ + { + "Ref": "LogGroupF5B46931" + } + ] + } } }, "Parameters": { diff --git a/packages/@aws-cdk/aws-logs/test/integ.save-logs-insights-query-definition.js.snapshot/cdk.out b/packages/@aws-cdk/aws-logs/test/integ.save-logs-insights-query-definition.js.snapshot/cdk.out index 8ecc185e9dbee..d8b441d447f8a 100644 --- a/packages/@aws-cdk/aws-logs/test/integ.save-logs-insights-query-definition.js.snapshot/cdk.out +++ b/packages/@aws-cdk/aws-logs/test/integ.save-logs-insights-query-definition.js.snapshot/cdk.out @@ -1 +1 @@ -{"version":"21.0.0"} \ No newline at end of file +{"version":"29.0.0"} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-logs/test/integ.save-logs-insights-query-definition.js.snapshot/integ.json b/packages/@aws-cdk/aws-logs/test/integ.save-logs-insights-query-definition.js.snapshot/integ.json index 5c5e893333693..f445dff967844 100644 --- a/packages/@aws-cdk/aws-logs/test/integ.save-logs-insights-query-definition.js.snapshot/integ.json +++ b/packages/@aws-cdk/aws-logs/test/integ.save-logs-insights-query-definition.js.snapshot/integ.json @@ -1,5 +1,5 @@ { - "version": "21.0.0", + "version": "29.0.0", "testCases": { "LogsInsightsQueryDefinitionIntegTest/DefaultTest": { "stacks": [ diff --git a/packages/@aws-cdk/aws-logs/test/integ.save-logs-insights-query-definition.js.snapshot/manifest.json b/packages/@aws-cdk/aws-logs/test/integ.save-logs-insights-query-definition.js.snapshot/manifest.json index d6fb5b02dcca8..a975ff9d5fae9 100644 --- a/packages/@aws-cdk/aws-logs/test/integ.save-logs-insights-query-definition.js.snapshot/manifest.json +++ b/packages/@aws-cdk/aws-logs/test/integ.save-logs-insights-query-definition.js.snapshot/manifest.json @@ -1,5 +1,5 @@ { - "version": "21.0.0", + "version": "29.0.0", "artifacts": { "aws-cdk-logs-insights-querydefinition-integ.assets": { "type": "cdk:asset-manifest", @@ -17,7 +17,7 @@ "validateOnSynth": false, "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", - "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/e6a0a51961925fbc37d9b81431449c256ed453f98089eb70f83850f237b4d722.json", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/3546c78647ea20567832041c960a034787f9bfc0128226ea8fbc0894366a4dd0.json", "requiresBootstrapStackVersion": 6, "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", "additionalDependencies": [ @@ -45,6 +45,12 @@ "data": "QueryDefinition4190BC36" } ], + "/aws-cdk-logs-insights-querydefinition-integ/QueryDefinitionWithMultipleStatements/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "QueryDefinitionWithMultipleStatements58A3EF74" + } + ], "/aws-cdk-logs-insights-querydefinition-integ/BootstrapVersion": [ { "type": "aws:cdk:logicalId", diff --git a/packages/@aws-cdk/aws-logs/test/integ.save-logs-insights-query-definition.js.snapshot/tree.json b/packages/@aws-cdk/aws-logs/test/integ.save-logs-insights-query-definition.js.snapshot/tree.json index 9e04ca4a9a7fa..1bbbc61ba797b 100644 --- a/packages/@aws-cdk/aws-logs/test/integ.save-logs-insights-query-definition.js.snapshot/tree.json +++ b/packages/@aws-cdk/aws-logs/test/integ.save-logs-insights-query-definition.js.snapshot/tree.json @@ -62,6 +62,36 @@ "version": "0.0.0" } }, + "QueryDefinitionWithMultipleStatements": { + "id": "QueryDefinitionWithMultipleStatements", + "path": "aws-cdk-logs-insights-querydefinition-integ/QueryDefinitionWithMultipleStatements", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-cdk-logs-insights-querydefinition-integ/QueryDefinitionWithMultipleStatements/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::Logs::QueryDefinition", + "aws:cdk:cloudformation:props": { + "name": "QueryDefinitionWithMultipleStatements", + "queryString": "fields @timestamp, @message\n| parse @message \"[*] *\" as loggingType, loggingMessage\n| parse @message \"<*>: *\" as differentLoggingType, differentLoggingMessage\n| filter loggingType = \"ERROR\"\n| filter loggingMessage = \"A very strange error occurred!\"\n| sort @timestamp desc\n| limit 20\n| display loggingMessage", + "logGroupNames": [ + { + "Ref": "LogGroupF5B46931" + } + ] + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-logs.CfnQueryDefinition", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-logs.QueryDefinition", + "version": "0.0.0" + } + }, "BootstrapVersion": { "id": "BootstrapVersion", "path": "aws-cdk-logs-insights-querydefinition-integ/BootstrapVersion", @@ -97,7 +127,7 @@ "path": "LogsInsightsQueryDefinitionIntegTest/DefaultTest/Default", "constructInfo": { "fqn": "constructs.Construct", - "version": "10.1.161" + "version": "10.1.237" } }, "DeployAssert": { @@ -143,7 +173,7 @@ "path": "Tree", "constructInfo": { "fqn": "constructs.Construct", - "version": "10.1.161" + "version": "10.1.237" } } }, From 02bfb5f6c276eb785ef2f9b17fd01dc21b7eaca4 Mon Sep 17 00:00:00 2001 From: Mike White Date: Tue, 14 Feb 2023 11:50:04 -0500 Subject: [PATCH 4/5] Addressing comments on PR. --- packages/@aws-cdk/aws-logs/README.md | 25 ++----- .../@aws-cdk/aws-logs/lib/query-definition.ts | 75 ++++++++++++++----- ...teg.save-logs-insights-query-definition.ts | 4 +- .../aws-logs/test/query-definition.test.ts | 38 +++++++++- 4 files changed, 98 insertions(+), 44 deletions(-) diff --git a/packages/@aws-cdk/aws-logs/README.md b/packages/@aws-cdk/aws-logs/README.md index 98b1a40ffcc49..5a6a28f74547d 100644 --- a/packages/@aws-cdk/aws-logs/README.md +++ b/packages/@aws-cdk/aws-logs/README.md @@ -330,31 +330,16 @@ new logs.QueryDefinition(this, 'QueryDefinition', { queryDefinitionName: 'MyQuery', queryString: new logs.QueryString({ fields: ['@timestamp', '@message'], - parse: '@message "[*] *" as loggingType, loggingMessage', - filter: 'loggingType = "ERROR"', - sort: '@timestamp desc', - limit: 20, - }), -}); -``` - -Query strings can also be constructed with multiple parse and filter statements if these are provided as an array. - -Example: - -```ts -new logs.QueryDefinition(this, 'QueryDefinition', { - queryDefinitionName: 'MyQuery', - queryString: new logs.QueryString({ - fields: ['@timestamp', '@message'], - parse: [ + parseStatements: [ '@message "[*] *" as loggingType, loggingMessage', '@message "<*>: *" as differentLoggingType, differentLoggingMessage', ], - filter: [ + filterStatements: [ 'loggingType = "ERROR"', 'loggingMessage = "A very strange error occurred!"', - ] + ], + sort: '@timestamp desc', + limit: 20, }), }); ``` diff --git a/packages/@aws-cdk/aws-logs/lib/query-definition.ts b/packages/@aws-cdk/aws-logs/lib/query-definition.ts index 4b5345d15179d..d3d49cf9f7ed9 100644 --- a/packages/@aws-cdk/aws-logs/lib/query-definition.ts +++ b/packages/@aws-cdk/aws-logs/lib/query-definition.ts @@ -16,21 +16,42 @@ export interface QueryStringProps { readonly fields?: string[]; /** - * One or more statements for parsing data from a log field and creating ephemeral fields that can - * be processed further in the query. Each statement provided generates a separate parse line in - * the query string. + * A single statement for parsing data from a log field and creating ephemeral fields that can + * be processed further in the query. * + * @deprecated Use `parseStatements` instead * @default - no parse in QueryString */ - readonly parse?: string | string[]; + readonly parse?: string; /** - * One or more statements for filtering the results of a query based on a boolean expression. Each - * statement provided generates a separate filter line in the query string. + * An array of one or more statements for parsing data from a log field and creating ephemeral + * fields that can be processed further in the query. Each provided statement generates a separate + * parse line in the query string. + * + * Note: If provided, this property overrides any value provided for the `parse` property. + * + * @default - no parse in QueryString + */ + readonly parseStatements?: string[]; + + /** + * A single statement for filtering the results of a query based on a boolean expression. + * + * @deprecated Use `filterStatements` instead + * @default - no filter in QueryString + */ + readonly filter?: string; + + /** + * An array of one or more statements for filtering the results of a query based on a boolean + * expression. Each provided statement generates a separate filter line in the query string. + * + * Note: If provided, this property overrides any value provided for the `filter` property. * * @default - no filter in QueryString */ - readonly filter?: string | string[]; + readonly filterStatements?: string[]; /** * Uses log field values to calculate aggregate statistics. @@ -66,8 +87,8 @@ export interface QueryStringProps { */ export class QueryString { private readonly fields?: string[]; - private readonly parse?: string | string[]; - private readonly filter?: string | string[]; + private readonly parse: string[]; + private readonly filter: string[]; private readonly stats?: string; private readonly sort?: string; private readonly limit?: Number; @@ -75,12 +96,28 @@ export class QueryString { 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; + + // Determine parsing by either the parseStatements or parse properties, or default to empty array + if (props.parseStatements) { + this.parse = props.parseStatements; + } else if (props.parse) { + this.parse = [props.parse]; + } else { + this.parse = []; + } + + // Determine filtering by either the filterStatements or filter properties, or default to empty array + if (props.filterStatements) { + this.filter = props.filterStatements; + } else if (props.filter) { + this.filter = [props.filter]; + } else { + this.filter = []; + } } /** @@ -88,13 +125,13 @@ export class QueryString { */ public toString(): string { return [ - ...this.buildQueryLines('fields', this.fields?.join(', ')), + this.buildQueryLine('fields', this.fields?.join(', ')), ...this.buildQueryLines('parse', this.parse), ...this.buildQueryLines('filter', this.filter), - ...this.buildQueryLines('stats', this.stats), - ...this.buildQueryLines('sort', this.sort), - ...this.buildQueryLines('limit', this.limit?.toString()), - ...this.buildQueryLines('display', this.display), + this.buildQueryLine('stats', this.stats), + this.buildQueryLine('sort', this.sort), + this.buildQueryLine('limit', this.limit?.toString()), + this.buildQueryLine('display', this.display), ].filter( (queryLine) => queryLine !== undefined && queryLine.length > 0, ).join('\n| '); @@ -107,13 +144,13 @@ export class QueryString { * @param statements one or more query statements for the specified command, or undefined * @returns an array of the query string lines generated from the provided command and statements */ - buildQueryLines(command: string, statements: string[] | string | undefined): string[] { + buildQueryLines(command: string, statements?: string[]): string[] { if (statements === undefined) { return []; } return (typeof statements === 'string' ? [statements] : statements).map( - (statement: string | undefined): string => this.buildQueryLine(command, statement!), + (statement: string): string => this.buildQueryLine(command, statement), ); } @@ -124,7 +161,7 @@ export class QueryString { * @param statement a single query statement * @returns a single query string line generated from the provided command and statement */ - buildQueryLine(command: string, statement: string): string { + buildQueryLine(command: string, statement?: string): string { return statement ? `${command} ${statement}` : ''; } } diff --git a/packages/@aws-cdk/aws-logs/test/integ.save-logs-insights-query-definition.ts b/packages/@aws-cdk/aws-logs/test/integ.save-logs-insights-query-definition.ts index 8be1ad7f0e970..ad022bcc486a7 100644 --- a/packages/@aws-cdk/aws-logs/test/integ.save-logs-insights-query-definition.ts +++ b/packages/@aws-cdk/aws-logs/test/integ.save-logs-insights-query-definition.ts @@ -30,11 +30,11 @@ class LogsInsightsQueryDefinitionIntegStack extends Stack { queryDefinitionName: 'QueryDefinitionWithMultipleStatements', queryString: new QueryString({ fields: ['@timestamp', '@message'], - parse: [ + parseStatements: [ '@message "[*] *" as loggingType, loggingMessage', '@message "<*>: *" as differentLoggingType, differentLoggingMessage', ], - filter: [ + filterStatements: [ 'loggingType = "ERROR"', 'loggingMessage = "A very strange error occurred!"', ], diff --git a/packages/@aws-cdk/aws-logs/test/query-definition.test.ts b/packages/@aws-cdk/aws-logs/test/query-definition.test.ts index a611f0c372665..10593be3c35f6 100644 --- a/packages/@aws-cdk/aws-logs/test/query-definition.test.ts +++ b/packages/@aws-cdk/aws-logs/test/query-definition.test.ts @@ -1,4 +1,5 @@ import { Template } from '@aws-cdk/assertions'; +import { testDeprecated } from '@aws-cdk/cdk-build-tools'; import { Stack } from '@aws-cdk/core'; import { LogGroup, QueryDefinition, QueryString } from '../lib'; @@ -49,7 +50,7 @@ describe('query definition', () => { }); }); - test('create a query definition with all commands', () => { + testDeprecated('create a query definition with all commands', () => { // GIVEN const stack = new Stack(); @@ -85,11 +86,42 @@ describe('query definition', () => { queryDefinitionName: 'MyQuery', queryString: new QueryString({ fields: ['@timestamp', '@message'], - parse: [ + parseStatements: [ '@message "[*] *" as loggingType, loggingMessage', '@message "<*>: *" as differentLoggingType, differentLoggingMessage', ], - filter: [ + filterStatements: [ + 'loggingType = "ERROR"', + 'loggingMessage = "A very strange error occurred!"', + ], + sort: '@timestamp desc', + limit: 20, + }), + }); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::Logs::QueryDefinition', { + Name: 'MyQuery', + QueryString: 'fields @timestamp, @message\n| parse @message "[*] *" as loggingType, loggingMessage\n| parse @message "<*>: *" as differentLoggingType, differentLoggingMessage\n| filter loggingType = "ERROR"\n| filter loggingMessage = "A very strange error occurred!"\n| sort @timestamp desc\n| limit 20', + }); + }); + + testDeprecated('create a query with both single and multi statement properties for filtering and parsing', () => { + // GIVEN + const stack = new Stack(); + + // WHEN + new QueryDefinition(stack, 'QueryDefinition', { + queryDefinitionName: 'MyQuery', + queryString: new QueryString({ + fields: ['@timestamp', '@message'], + parse: '@message "[*] *" as loggingType, loggingMessage', + parseStatements: [ + '@message "[*] *" as loggingType, loggingMessage', + '@message "<*>: *" as differentLoggingType, differentLoggingMessage', + ], + filter: 'loggingType = "ERROR"', + filterStatements: [ 'loggingType = "ERROR"', 'loggingMessage = "A very strange error occurred!"', ], From 9d3d1bf7e72f2042dbdbe7f04cef3e9ac05f5181 Mon Sep 17 00:00:00 2001 From: Mike White Date: Wed, 15 Feb 2023 14:34:24 -0500 Subject: [PATCH 5/5] Addressing comments on PR. --- packages/@aws-cdk/aws-logs/lib/query-definition.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/@aws-cdk/aws-logs/lib/query-definition.ts b/packages/@aws-cdk/aws-logs/lib/query-definition.ts index d3d49cf9f7ed9..09594756d4cb2 100644 --- a/packages/@aws-cdk/aws-logs/lib/query-definition.ts +++ b/packages/@aws-cdk/aws-logs/lib/query-definition.ts @@ -144,12 +144,12 @@ export class QueryString { * @param statements one or more query statements for the specified command, or undefined * @returns an array of the query string lines generated from the provided command and statements */ - buildQueryLines(command: string, statements?: string[]): string[] { + private buildQueryLines(command: string, statements?: string[]): string[] { if (statements === undefined) { return []; } - return (typeof statements === 'string' ? [statements] : statements).map( + return statements.map( (statement: string): string => this.buildQueryLine(command, statement), ); } @@ -161,7 +161,7 @@ export class QueryString { * @param statement a single query statement * @returns a single query string line generated from the provided command and statement */ - buildQueryLine(command: string, statement?: string): string { + private buildQueryLine(command: string, statement?: string): string { return statement ? `${command} ${statement}` : ''; } }