From 3f53a4551ea7d4f2b41d062b0220e071f219207f Mon Sep 17 00:00:00 2001 From: dali546 <35352237+dali546@users.noreply.github.com> Date: Thu, 30 May 2024 00:25:07 +0100 Subject: [PATCH] fix(lambda): use enum values for applicationLogLevel and systemLogLevel (#29904) ### Issue # (if applicable) ### Reason for this change Enumerate `ApplicationLogLevel` and `SystemLogLevel` to help with typing ### Description of changes Both fields should use the enum type for available options ### Description of how you validated changes ### Checklist - [x] My code adheres to the [CONTRIBUTING GUIDE](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md) and [DESIGN GUIDELINES](https://github.com/aws/aws-cdk/blob/main/docs/DESIGN_GUIDELINES.md) ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../aws-cdk-lambda-logging-config.assets.json | 4 +- ...ws-cdk-lambda-logging-config.template.json | 55 +++++++++ .../manifest.json | 14 ++- .../tree.json | 96 ++++++++++++++++ .../aws-lambda/test/integ.logging-config.ts | 9 ++ packages/aws-cdk-lib/aws-lambda/README.md | 12 +- .../aws-cdk-lib/aws-lambda/lib/function.ts | 42 +++++-- .../aws-lambda/test/logging-config.test.ts | 104 +++++++++++++++--- 8 files changed, 305 insertions(+), 31 deletions(-) diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-lambda/test/integ.logging-config.js.snapshot/aws-cdk-lambda-logging-config.assets.json b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda/test/integ.logging-config.js.snapshot/aws-cdk-lambda-logging-config.assets.json index 7c3b04d5c455e..11d9b3ad0358f 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-lambda/test/integ.logging-config.js.snapshot/aws-cdk-lambda-logging-config.assets.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda/test/integ.logging-config.js.snapshot/aws-cdk-lambda-logging-config.assets.json @@ -1,7 +1,7 @@ { "version": "36.0.0", "files": { - "04a0e9e899cf60a12349bd536e5e4764854e9e0c2e4bf28caf29641d28a1aabc": { + "ca1d6ec19cef9e3b887e11f0ef107dd32ef686db5e138eee03b83852d2fe7db5": { "source": { "path": "aws-cdk-lambda-logging-config.template.json", "packaging": "file" @@ -9,7 +9,7 @@ "destinations": { "current_account-current_region": { "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", - "objectKey": "04a0e9e899cf60a12349bd536e5e4764854e9e0c2e4bf28caf29641d28a1aabc.json", + "objectKey": "ca1d6ec19cef9e3b887e11f0ef107dd32ef686db5e138eee03b83852d2fe7db5.json", "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" } } diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-lambda/test/integ.logging-config.js.snapshot/aws-cdk-lambda-logging-config.template.json b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda/test/integ.logging-config.js.snapshot/aws-cdk-lambda-logging-config.template.json index 66aaf9cdff899..d477be4583206 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-lambda/test/integ.logging-config.js.snapshot/aws-cdk-lambda-logging-config.template.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda/test/integ.logging-config.js.snapshot/aws-cdk-lambda-logging-config.template.json @@ -393,6 +393,61 @@ "DependsOn": [ "LambdaWithLogLevelServiceRole90A45743" ] + }, + "LambdaWithLogLevelV2ServiceRoleD184382A": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "ManagedPolicyArns": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ] + ] + } + ] + } + }, + "LambdaWithLogLevelV2D4FB10AC": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "ZipFile": "foo" + }, + "Handler": "index.handler", + "LoggingConfig": { + "ApplicationLogLevel": "INFO", + "LogFormat": "JSON", + "SystemLogLevel": "INFO" + }, + "Role": { + "Fn::GetAtt": [ + "LambdaWithLogLevelV2ServiceRoleD184382A", + "Arn" + ] + }, + "Runtime": "nodejs18.x" + }, + "DependsOn": [ + "LambdaWithLogLevelV2ServiceRoleD184382A" + ] } }, "Parameters": { diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-lambda/test/integ.logging-config.js.snapshot/manifest.json b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda/test/integ.logging-config.js.snapshot/manifest.json index a9452713ec2e1..c708f873f9ab5 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-lambda/test/integ.logging-config.js.snapshot/manifest.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda/test/integ.logging-config.js.snapshot/manifest.json @@ -18,7 +18,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}/04a0e9e899cf60a12349bd536e5e4764854e9e0c2e4bf28caf29641d28a1aabc.json", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/ca1d6ec19cef9e3b887e11f0ef107dd32ef686db5e138eee03b83852d2fe7db5.json", "requiresBootstrapStackVersion": 6, "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", "additionalDependencies": [ @@ -130,6 +130,18 @@ "data": "LambdaWithLogLevelCBBBEFFC" } ], + "/aws-cdk-lambda-logging-config/LambdaWithLogLevelV2/ServiceRole/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "LambdaWithLogLevelV2ServiceRoleD184382A" + } + ], + "/aws-cdk-lambda-logging-config/LambdaWithLogLevelV2/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "LambdaWithLogLevelV2D4FB10AC" + } + ], "/aws-cdk-lambda-logging-config/BootstrapVersion": [ { "type": "aws:cdk:logicalId", diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-lambda/test/integ.logging-config.js.snapshot/tree.json b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda/test/integ.logging-config.js.snapshot/tree.json index 92e1464903af9..6087427154940 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-lambda/test/integ.logging-config.js.snapshot/tree.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda/test/integ.logging-config.js.snapshot/tree.json @@ -721,6 +721,102 @@ "version": "0.0.0" } }, + "LambdaWithLogLevelV2": { + "id": "LambdaWithLogLevelV2", + "path": "aws-cdk-lambda-logging-config/LambdaWithLogLevelV2", + "children": { + "ServiceRole": { + "id": "ServiceRole", + "path": "aws-cdk-lambda-logging-config/LambdaWithLogLevelV2/ServiceRole", + "children": { + "ImportServiceRole": { + "id": "ImportServiceRole", + "path": "aws-cdk-lambda-logging-config/LambdaWithLogLevelV2/ServiceRole/ImportServiceRole", + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + }, + "Resource": { + "id": "Resource", + "path": "aws-cdk-lambda-logging-config/LambdaWithLogLevelV2/ServiceRole/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Role", + "aws:cdk:cloudformation:props": { + "assumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "managedPolicyArns": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ] + ] + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.CfnRole", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.Role", + "version": "0.0.0" + } + }, + "Resource": { + "id": "Resource", + "path": "aws-cdk-lambda-logging-config/LambdaWithLogLevelV2/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::Lambda::Function", + "aws:cdk:cloudformation:props": { + "code": { + "zipFile": "foo" + }, + "handler": "index.handler", + "loggingConfig": { + "logFormat": "JSON", + "systemLogLevel": "INFO", + "applicationLogLevel": "INFO" + }, + "role": { + "Fn::GetAtt": [ + "LambdaWithLogLevelV2ServiceRoleD184382A", + "Arn" + ] + }, + "runtime": "nodejs18.x" + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_lambda.CfnFunction", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_lambda.Function", + "version": "0.0.0" + } + }, "BootstrapVersion": { "id": "BootstrapVersion", "path": "aws-cdk-lambda-logging-config/BootstrapVersion", diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-lambda/test/integ.logging-config.ts b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda/test/integ.logging-config.ts index 5488727ca8413..76454daa651ce 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-lambda/test/integ.logging-config.ts +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda/test/integ.logging-config.ts @@ -62,6 +62,15 @@ new Function(stack, 'LambdaWithLogLevel', { applicationLogLevel: ApplicationLogLevel.INFO, }); +new Function(stack, 'LambdaWithLogLevelV2', { + code: new InlineCode('foo'), + handler: 'index.handler', + runtime: Runtime.NODEJS_18_X, + loggingFormat: LoggingFormat.JSON, + systemLogLevelV2: SystemLogLevel.INFO, + applicationLogLevelV2: ApplicationLogLevel.INFO, +}); + new integ.IntegTest(app, 'lambda-logging-config', { testCases: [stack], }); diff --git a/packages/aws-cdk-lib/aws-lambda/README.md b/packages/aws-cdk-lib/aws-lambda/README.md index 5a876c8ae68eb..bcf108b51d1ef 100644 --- a/packages/aws-cdk-lib/aws-lambda/README.md +++ b/packages/aws-cdk-lib/aws-lambda/README.md @@ -160,20 +160,20 @@ as choosing the log group: ```ts import { ILogGroup } from 'aws-cdk-lib/aws-logs'; -declare const logGroup: ILogGroup; +declare const logGroup: ILogGroup; new lambda.Function(this, 'Lambda', { code: new lambda.InlineCode('foo'), handler: 'index.handler', runtime: lambda.Runtime.NODEJS_18_X, loggingFormat: lambda.LoggingFormat.JSON, - systemLogLevel: lambda.SystemLogLevel.INFO, - applicationLogLevel: lambda.ApplicationLogLevel.INFO, + systemLogLevelV2: lambda.SystemLogLevel.INFO, + applicationLogLevelV2: lambda.ApplicationLogLevel.INFO, logGroup: logGroup, }); ``` -To use `applicationLogLevel` and/or `systemLogLevel` you must set `loggingFormat` to `LoggingFormat.JSON`. +To use `applicationLogLevelV2` and/or `systemLogLevelV2` you must set `loggingFormat` to `LoggingFormat.JSON`. ## Resource-based Policies @@ -1077,8 +1077,8 @@ const fn = new lambda.Function(this, 'MyLambda', { ## IPv6 support -You can configure IPv6 connectivity for lambda function by setting `Ipv6AllowedForDualStack` to true. -It allows Lambda functions to specify whether the IPv6 traffic should be allowed when using dual-stack VPCs. +You can configure IPv6 connectivity for lambda function by setting `Ipv6AllowedForDualStack` to true. +It allows Lambda functions to specify whether the IPv6 traffic should be allowed when using dual-stack VPCs. To access IPv6 network using Lambda, Dual-stack VPC is required. Using dual-stack VPC a function communicates with subnet over either of IPv4 or IPv6. ```ts diff --git a/packages/aws-cdk-lib/aws-lambda/lib/function.ts b/packages/aws-cdk-lib/aws-lambda/lib/function.ts index dba046f447513..32e477f81f86b 100644 --- a/packages/aws-cdk-lib/aws-lambda/lib/function.ts +++ b/packages/aws-cdk-lib/aws-lambda/lib/function.ts @@ -524,6 +524,7 @@ export interface FunctionOptions extends EventInvokeConfigOptions { /** * Sets the logFormat for the function. + * @deprecated Use `loggingFormat` as a property instead. * @default "Text" */ readonly logFormat?: string; @@ -536,15 +537,29 @@ export interface FunctionOptions extends EventInvokeConfigOptions { /** * Sets the application log level for the function. + * @deprecated Use `applicationLogLevelV2` as a property instead. * @default "INFO" */ readonly applicationLogLevel?: string; + /** + * Sets the application log level for the function. + * @default ApplicationLogLevel.INFO + */ + readonly applicationLogLevelV2?: ApplicationLogLevel; + /** * Sets the system log level for the function. + * @deprecated Use `systemLogLevelV2` as a property instead. * @default "INFO" */ readonly systemLogLevel?: string; + + /** + * Sets the system log level for the function. + * @default SystemLogLevel.INFO + */ + readonly systemLogLevelV2?: SystemLogLevel; } export interface FunctionProps extends FunctionOptions { @@ -1151,25 +1166,34 @@ export class Function extends FunctionBase { * function and undefined if not. */ private getLoggingConfig(props: FunctionProps): CfnFunction.LoggingConfigProperty | undefined { - if ((props.applicationLogLevel || props.systemLogLevel) && props.logFormat !== LogFormat.JSON - && props.loggingFormat === undefined) { - throw new Error(`To use ApplicationLogLevel and/or SystemLogLevel you must set LogFormat to '${LogFormat.JSON}', got '${props.logFormat}'.`); + if (props.logFormat && props.loggingFormat) { + throw new Error('Only define LogFormat or LoggingFormat, not both.'); } - if ((props.applicationLogLevel || props.systemLogLevel) && props.loggingFormat !== LoggingFormat.JSON && props.logFormat === undefined) { - throw new Error(`To use ApplicationLogLevel and/or SystemLogLevel you must set LoggingFormat to '${LoggingFormat.JSON}', got '${props.loggingFormat}'.`); + if (props.applicationLogLevel && props.applicationLogLevelV2) { + throw new Error('Only define applicationLogLevel or applicationLogLevelV2, not both.'); } - if (props.logFormat && props.loggingFormat) { - throw new Error('Only define LogFormat or LoggingFormat, not both.'); + if (props.systemLogLevel && props.systemLogLevelV2) { + throw new Error('Only define systemLogLevel or systemLogLevelV2, not both.'); + } + + if (props.applicationLogLevel || props.applicationLogLevelV2 || props.systemLogLevel || props.systemLogLevelV2) { + if (props.logFormat !== LoggingFormat.JSON && props.loggingFormat === undefined) { + throw new Error(`To use ApplicationLogLevel and/or SystemLogLevel you must set LogFormat to '${LogFormat.JSON}', got '${props.logFormat}'.`); + } + + if (props.loggingFormat !== LoggingFormat.JSON && props.logFormat === undefined) { + throw new Error(`To use ApplicationLogLevel and/or SystemLogLevel you must set LoggingFormat to '${LoggingFormat.JSON}', got '${props.loggingFormat}'.`); + } } let loggingConfig: CfnFunction.LoggingConfigProperty; if (props.logFormat || props.logGroup || props.loggingFormat) { loggingConfig = { logFormat: props.logFormat || props.loggingFormat, - systemLogLevel: props.systemLogLevel, - applicationLogLevel: props.applicationLogLevel, + systemLogLevel: props.systemLogLevel || props.systemLogLevelV2, + applicationLogLevel: props.applicationLogLevel || props.applicationLogLevelV2, logGroup: props.logGroup?.logGroupName, }; return loggingConfig; diff --git a/packages/aws-cdk-lib/aws-lambda/test/logging-config.test.ts b/packages/aws-cdk-lib/aws-lambda/test/logging-config.test.ts index 7c8f0e861c850..ae16882c1523c 100644 --- a/packages/aws-cdk-lib/aws-lambda/test/logging-config.test.ts +++ b/packages/aws-cdk-lib/aws-lambda/test/logging-config.test.ts @@ -142,6 +142,28 @@ describe('logging Config', () => { }); }); + test('Logging Config with LogLevel set with enum keys', () => { + // GIVEN + const app = new cdk.App(); + const stack = new cdk.Stack(app, 'stack'); + new lambda.Function(stack, 'Lambda', { + code: new lambda.InlineCode('foo'), + handler: 'index.handler', + runtime: lambda.Runtime.NODEJS_18_X, + loggingFormat: lambda.LoggingFormat.JSON, + systemLogLevelV2: lambda.SystemLogLevel.INFO, + applicationLogLevelV2: lambda.ApplicationLogLevel.INFO, + }); + // WHEN + Template.fromStack(stack).hasResourceProperties('AWS::Lambda::Function', { + LoggingConfig: { + LogFormat: 'JSON', + SystemLogLevel: 'INFO', + ApplicationLogLevel: 'INFO', + }, + }); + }); + test('Get function custom logGroup', () => { // GIVEN const app = new cdk.App(); @@ -243,18 +265,74 @@ describe('logging Config', () => { }); }).toThrow(/To use ApplicationLogLevel and\/or SystemLogLevel you must set LogFormat to 'JSON', got 'undefined'./); }); -}); -test('Throws when loggingFormat and logFormat are both specified', () => { - const app = new cdk.App(); - const stack = new cdk.Stack(app, 'stack'); - expect(() => { - new lambda.Function(stack, 'Lambda', { - code: new lambda.InlineCode('foo'), - handler: 'index.handler', - runtime: lambda.Runtime.NODEJS_18_X, - loggingFormat: lambda.LoggingFormat.JSON, - logFormat: lambda.LogFormat.TEXT, - }); - }).toThrow(/Only define LogFormat or LoggingFormat, not both./); + test('Throws when loggingFormat and logFormat are both specified', () => { + const app = new cdk.App(); + const stack = new cdk.Stack(app, 'stack'); + expect(() => { + new lambda.Function(stack, 'Lambda', { + code: new lambda.InlineCode('foo'), + handler: 'index.handler', + runtime: lambda.Runtime.NODEJS_18_X, + loggingFormat: lambda.LoggingFormat.JSON, + logFormat: lambda.LogFormat.TEXT, + }); + }).toThrow(/Only define LogFormat or LoggingFormat, not both./); + }); + + test('Throws when applicationLogLevel and applicationLogLevelV2 are both specified', () => { + const app = new cdk.App(); + const stack = new cdk.Stack(app, 'stack'); + expect(() => { + new lambda.Function(stack, 'Lambda', { + code: new lambda.InlineCode('foo'), + handler: 'index.handler', + runtime: lambda.Runtime.NODEJS_18_X, + loggingFormat: lambda.LoggingFormat.JSON, + applicationLogLevel: lambda.ApplicationLogLevel.INFO, + applicationLogLevelV2: lambda.ApplicationLogLevel.WARN, + }); + }).toThrow(/Only define applicationLogLevel or applicationLogLevelV2, not both./); + }); + + test('Throws when systemLogLevel and systemLogLevelV2 are both specified', () => { + const app = new cdk.App(); + const stack = new cdk.Stack(app, 'stack'); + expect(() => { + new lambda.Function(stack, 'Lambda', { + code: new lambda.InlineCode('foo'), + handler: 'index.handler', + runtime: lambda.Runtime.NODEJS_18_X, + loggingFormat: lambda.LoggingFormat.JSON, + systemLogLevel: lambda.SystemLogLevel.INFO, + systemLogLevelV2: lambda.SystemLogLevel.WARN, + }); + }).toThrow(/Only define systemLogLevel or systemLogLevelV2, not both./); + }); + + test('Throws when systemLogLevelV2 is specified if loggingFormat is undefined', () => { + const app = new cdk.App(); + const stack = new cdk.Stack(app, 'stack'); + expect(() => { + new lambda.Function(stack, 'Lambda', { + code: new lambda.InlineCode('foo'), + handler: 'index.handler', + runtime: lambda.Runtime.NODEJS_18_X, + systemLogLevelV2: lambda.SystemLogLevel.INFO, + }); + }).toThrow(/To use ApplicationLogLevel and\/or SystemLogLevel you must set LogFormat to 'JSON', got 'undefined'./); + }); + + test('Throws when applicationLogLevelV2 is specified if loggingFormat is undefined', () => { + const app = new cdk.App(); + const stack = new cdk.Stack(app, 'stack'); + expect(() => { + new lambda.Function(stack, 'Lambda', { + code: new lambda.InlineCode('foo'), + handler: 'index.handler', + runtime: lambda.Runtime.NODEJS_18_X, + applicationLogLevelV2: lambda.ApplicationLogLevel.INFO, + }); + }).toThrow(/To use ApplicationLogLevel and\/or SystemLogLevel you must set LogFormat to 'JSON', got 'undefined'./); + }); });