diff --git a/.changeset/poor-moons-refuse.md b/.changeset/poor-moons-refuse.md
new file mode 100644
index 00000000000..4da456357c7
--- /dev/null
+++ b/.changeset/poor-moons-refuse.md
@@ -0,0 +1,9 @@
+---
+'@aws-amplify/ai-constructs': minor
+'@aws-amplify/backend-ai': minor
+'@aws-amplify/backend-function': minor
+'@aws-amplify/backend': minor
+'@aws-amplify/platform-core': minor
+---
+
+Add options to control log settings
diff --git a/package-lock.json b/package-lock.json
index abf7f393397..c60a804d40e 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -32195,6 +32195,10 @@
"@types/is-ci": "^3.0.4",
"@types/lodash.mergewith": "^4.6.2",
"@types/uuid": "9.0.7"
+ },
+ "peerDependencies": {
+ "aws-cdk-lib": "^2.168.0",
+ "constructs": "^10.0.0"
}
},
"packages/platform-core/node_modules/uuid": {
diff --git a/packages/ai-constructs/API.md b/packages/ai-constructs/API.md
index a5621d37df5..0f1550885f2 100644
--- a/packages/ai-constructs/API.md
+++ b/packages/ai-constructs/API.md
@@ -7,12 +7,14 @@
///
import { AIConversationOutput } from '@aws-amplify/backend-output-schemas';
+import { ApplicationLogLevel } from 'aws-cdk-lib/aws-lambda';
import { BackendOutputStorageStrategy } from '@aws-amplify/plugin-types';
import * as bedrock from '@aws-sdk/client-bedrock-runtime';
import { Construct } from 'constructs';
import { FunctionResources } from '@aws-amplify/plugin-types';
import * as jsonSchemaToTypeScript from 'json-schema-to-ts';
import { ResourceProvider } from '@aws-amplify/plugin-types';
+import { RetentionDays } from 'aws-cdk-lib/aws-logs';
declare namespace __export__conversation {
export {
@@ -55,6 +57,10 @@ type ConversationHandlerFunctionProps = {
region?: string;
}>;
memoryMB?: number;
+ logging?: {
+ level?: ApplicationLogLevel;
+ retention?: RetentionDays;
+ };
outputStorageStrategy?: BackendOutputStorageStrategy;
};
diff --git a/packages/ai-constructs/src/conversation/conversation_handler_construct.test.ts b/packages/ai-constructs/src/conversation/conversation_handler_construct.test.ts
index b0130e711f9..284024a89e4 100644
--- a/packages/ai-constructs/src/conversation/conversation_handler_construct.test.ts
+++ b/packages/ai-constructs/src/conversation/conversation_handler_construct.test.ts
@@ -5,6 +5,8 @@ import { ConversationHandlerFunction } from './conversation_handler_construct';
import { Template } from 'aws-cdk-lib/assertions';
import path from 'path';
import { StackMetadataBackendOutputStorageStrategy } from '@aws-amplify/backend-output-storage';
+import { ApplicationLogLevel } from 'aws-cdk-lib/aws-lambda';
+import { RetentionDays } from 'aws-cdk-lib/aws-logs';
void describe('Conversation Handler Function construct', () => {
void it('creates handler with log group with JWT token redacting policy', () => {
@@ -284,4 +286,41 @@ void describe('Conversation Handler Function construct', () => {
}, new Error('memoryMB must be a whole number between 128 and 10240 inclusive'));
});
});
+
+ void describe('logging options', () => {
+ void it('sets log level', () => {
+ const app = new App();
+ const stack = new Stack(app);
+ new ConversationHandlerFunction(stack, 'conversationHandler', {
+ models: [],
+ logging: {
+ level: ApplicationLogLevel.DEBUG,
+ },
+ });
+ const template = Template.fromStack(stack);
+
+ template.hasResourceProperties('AWS::Lambda::Function', {
+ LoggingConfig: {
+ ApplicationLogLevel: 'DEBUG',
+ LogFormat: 'JSON',
+ },
+ });
+ });
+
+ void it('sets log retention', () => {
+ const app = new App();
+ const stack = new Stack(app);
+ new ConversationHandlerFunction(stack, 'conversationHandler', {
+ models: [],
+ logging: {
+ retention: RetentionDays.ONE_YEAR,
+ },
+ });
+ const template = Template.fromStack(stack);
+
+ template.hasResourceProperties('AWS::Logs::LogGroup', {
+ RetentionInDays: 365,
+ });
+ });
+ });
});
diff --git a/packages/ai-constructs/src/conversation/conversation_handler_construct.ts b/packages/ai-constructs/src/conversation/conversation_handler_construct.ts
index 995b92fed6c..e5b563a6df9 100644
--- a/packages/ai-constructs/src/conversation/conversation_handler_construct.ts
+++ b/packages/ai-constructs/src/conversation/conversation_handler_construct.ts
@@ -6,6 +6,7 @@ import {
import { Duration, Stack, Tags } from 'aws-cdk-lib';
import { Effect, PolicyStatement } from 'aws-cdk-lib/aws-iam';
import {
+ ApplicationLogLevel,
CfnFunction,
Runtime as LambdaRuntime,
LoggingFormat,
@@ -40,6 +41,12 @@ export type ConversationHandlerFunctionProps = {
* Default is 512MB.
*/
memoryMB?: number;
+
+ logging?: {
+ level?: ApplicationLogLevel;
+ retention?: RetentionDays;
+ };
+
/**
* @internal
*/
@@ -100,8 +107,9 @@ export class ConversationHandlerFunction
bundleAwsSDK: !!this.props.entry,
},
loggingFormat: LoggingFormat.JSON,
+ applicationLogLevelV2: this.props.logging?.level,
logGroup: new LogGroup(this, 'conversationHandlerFunctionLogGroup', {
- retention: RetentionDays.INFINITE,
+ retention: this.props.logging?.retention ?? RetentionDays.INFINITE,
dataProtectionPolicy: new DataProtectionPolicy({
identifiers: [
new CustomDataIdentifier(
diff --git a/packages/backend-ai/API.md b/packages/backend-ai/API.md
index 2b75a01c579..fcacb75c61c 100644
--- a/packages/backend-ai/API.md
+++ b/packages/backend-ai/API.md
@@ -8,12 +8,17 @@ import { AiModel } from '@aws-amplify/data-schema-types';
import { ConstructFactory } from '@aws-amplify/plugin-types';
import { ConversationTurnEventVersion } from '@aws-amplify/ai-constructs/conversation';
import { FunctionResources } from '@aws-amplify/plugin-types';
+import { LogLevel } from '@aws-amplify/plugin-types';
+import { LogRetention } from '@aws-amplify/plugin-types';
import { ResourceProvider } from '@aws-amplify/plugin-types';
import * as runtime from '@aws-amplify/ai-constructs/conversation/runtime';
declare namespace __export__conversation {
export {
ConversationHandlerFunctionFactory,
+ ConversationHandlerFunctionLogLevel,
+ ConversationHandlerFunctionLogRetention,
+ ConversationHandlerFunctionLoggingOptions,
DefineConversationHandlerFunctionProps,
defineConversationHandlerFunction
}
@@ -36,6 +41,18 @@ type ConversationHandlerFunctionFactory = ConstructFactory;
memoryMB?: number;
+ logging?: ConversationHandlerFunctionLoggingOptions;
};
// @public (undocumented)
diff --git a/packages/backend-ai/src/conversation/factory.test.ts b/packages/backend-ai/src/conversation/factory.test.ts
index 9802e4944bc..a264731ffb2 100644
--- a/packages/backend-ai/src/conversation/factory.test.ts
+++ b/packages/backend-ai/src/conversation/factory.test.ts
@@ -203,4 +203,41 @@ void describe('ConversationHandlerFactory', () => {
MemorySize: 271,
});
});
+
+ void it('passes log level to construct', () => {
+ const factory = defineConversationHandlerFunction({
+ entry: './test-assets/with-default-entry/handler.ts',
+ name: 'testHandlerName',
+ models: [],
+ logging: {
+ level: 'debug',
+ },
+ });
+ const lambda = factory.getInstance(getInstanceProps);
+ const template = Template.fromStack(Stack.of(lambda.resources.lambda));
+ template.resourceCountIs('AWS::Lambda::Function', 1);
+ template.hasResourceProperties('AWS::Lambda::Function', {
+ LoggingConfig: {
+ ApplicationLogLevel: 'DEBUG',
+ LogFormat: 'JSON',
+ },
+ });
+ });
+
+ void it('passes log retention to construct', () => {
+ const factory = defineConversationHandlerFunction({
+ entry: './test-assets/with-default-entry/handler.ts',
+ name: 'testHandlerName',
+ models: [],
+ logging: {
+ retention: '1 day',
+ },
+ });
+ const lambda = factory.getInstance(getInstanceProps);
+ const template = Template.fromStack(Stack.of(lambda.resources.lambda));
+ template.resourceCountIs('AWS::Lambda::Function', 1);
+ template.hasResourceProperties('AWS::Logs::LogGroup', {
+ RetentionInDays: 1,
+ });
+ });
});
diff --git a/packages/backend-ai/src/conversation/factory.ts b/packages/backend-ai/src/conversation/factory.ts
index 6b4242288ff..d5a88c3f03e 100644
--- a/packages/backend-ai/src/conversation/factory.ts
+++ b/packages/backend-ai/src/conversation/factory.ts
@@ -7,6 +7,8 @@ import {
ConstructFactoryGetInstanceProps,
FunctionResources,
GenerateContainerEntryProps,
+ LogLevel,
+ LogRetention,
ResourceProvider,
} from '@aws-amplify/plugin-types';
import {
@@ -17,6 +19,10 @@ import {
import path from 'path';
import { CallerDirectoryExtractor } from '@aws-amplify/platform-core';
import { AiModel } from '@aws-amplify/data-schema-types';
+import {
+ LogLevelConverter,
+ LogRetentionConverter,
+} from '@aws-amplify/platform-core/cdk';
class ConversationHandlerFunctionGenerator
implements ConstructContainerEntryGenerator
@@ -47,6 +53,18 @@ class ConversationHandlerFunctionGenerator
outputStorageStrategy: this.outputStorageStrategy,
memoryMB: this.props.memoryMB,
};
+ const logging: typeof constructProps.logging = {};
+ if (this.props.logging?.level) {
+ logging.level = new LogLevelConverter().toCDKLambdaApplicationLogLevel(
+ this.props.logging.level
+ );
+ }
+ if (this.props.logging?.retention) {
+ logging.retention = new LogRetentionConverter().toCDKRetentionDays(
+ this.props.logging.retention
+ );
+ }
+ constructProps.logging = logging;
const conversationHandlerFunction = new ConversationHandlerFunction(
scope,
this.props.name,
@@ -115,6 +133,15 @@ class DefaultConversationHandlerFunctionFactory
};
}
+export type ConversationHandlerFunctionLogLevel = LogLevel;
+
+export type ConversationHandlerFunctionLogRetention = LogRetention;
+
+export type ConversationHandlerFunctionLoggingOptions = {
+ retention?: ConversationHandlerFunctionLogRetention;
+ level?: ConversationHandlerFunctionLogLevel;
+};
+
export type DefineConversationHandlerFunctionProps = {
name: string;
entry?: string;
@@ -128,6 +155,7 @@ export type DefineConversationHandlerFunctionProps = {
* Default is 512MB.
*/
memoryMB?: number;
+ logging?: ConversationHandlerFunctionLoggingOptions;
};
/**
diff --git a/packages/backend-ai/src/conversation/index.ts b/packages/backend-ai/src/conversation/index.ts
index 489209c219d..69eb8d3c5cb 100644
--- a/packages/backend-ai/src/conversation/index.ts
+++ b/packages/backend-ai/src/conversation/index.ts
@@ -1,11 +1,17 @@
import {
ConversationHandlerFunctionFactory,
+ ConversationHandlerFunctionLogLevel,
+ ConversationHandlerFunctionLogRetention,
+ ConversationHandlerFunctionLoggingOptions,
DefineConversationHandlerFunctionProps,
defineConversationHandlerFunction,
} from './factory.js';
export {
ConversationHandlerFunctionFactory,
+ ConversationHandlerFunctionLogLevel,
+ ConversationHandlerFunctionLogRetention,
+ ConversationHandlerFunctionLoggingOptions,
DefineConversationHandlerFunctionProps,
defineConversationHandlerFunction,
};
diff --git a/packages/backend-function/API.md b/packages/backend-function/API.md
index 8e0ad66d853..500972914db 100644
--- a/packages/backend-function/API.md
+++ b/packages/backend-function/API.md
@@ -8,6 +8,8 @@ import { AmplifyResourceGroupName } from '@aws-amplify/plugin-types';
import { BackendSecret } from '@aws-amplify/plugin-types';
import { ConstructFactory } from '@aws-amplify/plugin-types';
import { FunctionResources } from '@aws-amplify/plugin-types';
+import { LogLevel } from '@aws-amplify/plugin-types';
+import { LogRetention } from '@aws-amplify/plugin-types';
import { ResourceAccessAcceptorFactory } from '@aws-amplify/plugin-types';
import { ResourceProvider } from '@aws-amplify/plugin-types';
import { StackProvider } from '@aws-amplify/plugin-types';
@@ -28,6 +30,22 @@ export type FunctionBundlingOptions = {
minify?: boolean;
};
+// @public (undocumented)
+export type FunctionLoggingOptions = ({
+ format: 'json';
+ level?: FunctionLogLevel;
+} | {
+ format?: 'text';
+}) & {
+ retention?: FunctionLogRetention;
+};
+
+// @public (undocumented)
+export type FunctionLogLevel = LogLevel;
+
+// @public (undocumented)
+export type FunctionLogRetention = LogRetention;
+
// @public (undocumented)
export type FunctionProps = {
name?: string;
@@ -40,6 +58,7 @@ export type FunctionProps = {
layers?: Record;
bundling?: FunctionBundlingOptions;
resourceGroupName?: AmplifyResourceGroupName;
+ logging?: FunctionLoggingOptions;
};
// @public (undocumented)
diff --git a/packages/backend-function/src/factory.test.ts b/packages/backend-function/src/factory.test.ts
index c7ce48a2cfe..8b964f336c2 100644
--- a/packages/backend-function/src/factory.test.ts
+++ b/packages/backend-function/src/factory.test.ts
@@ -440,6 +440,39 @@ void describe('AmplifyFunctionFactory', () => {
});
});
+ void describe('logging options', () => {
+ void it('sets logging options', () => {
+ const lambda = defineFunction({
+ entry: './test-assets/default-lambda/handler.ts',
+ bundling: {
+ minify: false,
+ },
+ logging: {
+ format: 'json',
+ level: 'warn',
+ retention: '13 months',
+ },
+ }).getInstance(getInstanceProps);
+ const template = Template.fromStack(lambda.stack);
+ // Enabling log retention adds extra lambda.
+ template.resourceCountIs('AWS::Lambda::Function', 2);
+ const lambdas = template.findResources('AWS::Lambda::Function');
+ assert.ok(
+ Object.keys(lambdas).some((key) => key.startsWith('LogRetention'))
+ );
+ template.hasResourceProperties('Custom::LogRetention', {
+ RetentionInDays: 400,
+ });
+ template.hasResourceProperties('AWS::Lambda::Function', {
+ Handler: 'index.handler',
+ LoggingConfig: {
+ ApplicationLogLevel: 'WARN',
+ LogFormat: 'JSON',
+ },
+ });
+ });
+ });
+
void describe('resourceAccessAcceptor', () => {
void it('attaches policy to execution role and configures ssm environment context', () => {
const functionFactory = defineFunction({
diff --git a/packages/backend-function/src/factory.ts b/packages/backend-function/src/factory.ts
index 9c29357c217..e752b46d44e 100644
--- a/packages/backend-function/src/factory.ts
+++ b/packages/backend-function/src/factory.ts
@@ -18,6 +18,8 @@ import {
ConstructFactoryGetInstanceProps,
FunctionResources,
GenerateContainerEntryProps,
+ LogLevel,
+ LogRetention,
ResourceAccessAcceptorFactory,
ResourceNameValidator,
ResourceProvider,
@@ -45,6 +47,7 @@ import { FunctionEnvironmentTranslator } from './function_env_translator.js';
import { FunctionEnvironmentTypeGenerator } from './function_env_type_generator.js';
import { FunctionLayerArnParser } from './layer_parser.js';
import { convertFunctionSchedulesToRuleSchedules } from './schedule_parser.js';
+import { convertLoggingOptionsToCDK } from './logging_options_parser.js';
const functionStackType = 'function-Lambda';
@@ -64,6 +67,9 @@ export type TimeInterval =
| `every year`;
export type FunctionSchedule = TimeInterval | CronSchedule;
+export type FunctionLogLevel = LogLevel;
+export type FunctionLogRetention = LogRetention;
+
/**
* Entry point for defining a function in the Amplify ecosystem
*/
@@ -159,6 +165,8 @@ export type FunctionProps = {
* resourceGroupName: 'auth' // to group an auth trigger with an auth resource
*/
resourceGroupName?: AmplifyResourceGroupName;
+
+ logging?: FunctionLoggingOptions;
};
export type FunctionBundlingOptions = {
@@ -170,6 +178,18 @@ export type FunctionBundlingOptions = {
minify?: boolean;
};
+export type FunctionLoggingOptions = (
+ | {
+ format: 'json';
+ level?: FunctionLogLevel;
+ }
+ | {
+ format?: 'text';
+ }
+) & {
+ retention?: FunctionLogRetention;
+};
+
/**
* Create Lambda functions in the context of an Amplify backend definition
*/
@@ -218,6 +238,7 @@ class FunctionFactory implements ConstructFactory {
bundling: this.resolveBundling(),
layers,
resourceGroupName: this.props.resourceGroupName ?? 'function',
+ logging: this.props.logging ?? {},
};
};
@@ -438,6 +459,7 @@ class AmplifyFunction
functionEnvironmentTypeGenerator.generateProcessEnvShim();
let functionLambda: NodejsFunction;
+ const cdkLoggingOptions = convertLoggingOptionsToCDK(props.logging);
try {
functionLambda = new NodejsFunction(scope, `${id}-lambda`, {
entry: props.entry,
@@ -451,6 +473,9 @@ class AmplifyFunction
inject: shims,
externalModules: Object.keys(props.layers),
},
+ logRetention: cdkLoggingOptions.retention,
+ applicationLogLevelV2: cdkLoggingOptions.level,
+ loggingFormat: cdkLoggingOptions.format,
});
} catch (error) {
// If the error is from ES Bundler which is executed as a child process by CDK,
diff --git a/packages/backend-function/src/logging_options_parser.test.ts b/packages/backend-function/src/logging_options_parser.test.ts
new file mode 100644
index 00000000000..4d6abec9589
--- /dev/null
+++ b/packages/backend-function/src/logging_options_parser.test.ts
@@ -0,0 +1,56 @@
+import { describe, it } from 'node:test';
+import assert from 'node:assert';
+import { FunctionLoggingOptions } from './factory.js';
+import {
+ CDKLoggingOptions,
+ convertLoggingOptionsToCDK,
+} from './logging_options_parser.js';
+import { ApplicationLogLevel, LoggingFormat } from 'aws-cdk-lib/aws-lambda';
+import { RetentionDays } from 'aws-cdk-lib/aws-logs';
+
+type TestCase = {
+ input: FunctionLoggingOptions;
+ expectedOutput: CDKLoggingOptions;
+};
+
+const testCases: Array = [
+ {
+ input: {},
+ expectedOutput: {
+ format: undefined,
+ level: undefined,
+ retention: undefined,
+ },
+ },
+ {
+ input: {
+ format: 'text',
+ retention: '13 months',
+ },
+ expectedOutput: {
+ format: LoggingFormat.TEXT,
+ retention: RetentionDays.THIRTEEN_MONTHS,
+ level: undefined,
+ },
+ },
+ {
+ input: {
+ format: 'json',
+ level: 'debug',
+ },
+ expectedOutput: {
+ format: LoggingFormat.JSON,
+ retention: undefined,
+ level: ApplicationLogLevel.DEBUG,
+ },
+ },
+];
+
+void describe('LoggingOptions converter', () => {
+ testCases.forEach((testCase, index) => {
+ void it(`converts to cdk options[${index}]`, () => {
+ const convertedOptions = convertLoggingOptionsToCDK(testCase.input);
+ assert.deepStrictEqual(convertedOptions, testCase.expectedOutput);
+ });
+ });
+});
diff --git a/packages/backend-function/src/logging_options_parser.ts b/packages/backend-function/src/logging_options_parser.ts
new file mode 100644
index 00000000000..ce5693d6253
--- /dev/null
+++ b/packages/backend-function/src/logging_options_parser.ts
@@ -0,0 +1,48 @@
+import { FunctionLoggingOptions } from './factory.js';
+import { ApplicationLogLevel, LoggingFormat } from 'aws-cdk-lib/aws-lambda';
+import {
+ LogLevelConverter,
+ LogRetentionConverter,
+} from '@aws-amplify/platform-core/cdk';
+import { RetentionDays } from 'aws-cdk-lib/aws-logs';
+
+export type CDKLoggingOptions = {
+ level?: ApplicationLogLevel;
+ retention?: RetentionDays;
+ format?: LoggingFormat;
+};
+
+/**
+ * Converts logging options to CDK.
+ */
+export const convertLoggingOptionsToCDK = (
+ loggingOptions: FunctionLoggingOptions
+): CDKLoggingOptions => {
+ let level: ApplicationLogLevel | undefined = undefined;
+ if ('level' in loggingOptions) {
+ level = new LogLevelConverter().toCDKLambdaApplicationLogLevel(
+ loggingOptions.level
+ );
+ }
+ const retention = new LogRetentionConverter().toCDKRetentionDays(
+ loggingOptions.retention
+ );
+ const format = convertFormat(loggingOptions.format);
+
+ return {
+ level,
+ retention,
+ format,
+ };
+};
+
+const convertFormat = (format: 'json' | 'text' | undefined) => {
+ switch (format) {
+ case undefined:
+ return undefined;
+ case 'json':
+ return LoggingFormat.JSON;
+ case 'text':
+ return LoggingFormat.TEXT;
+ }
+};
diff --git a/packages/platform-core/API.md b/packages/platform-core/API.md
index 286ae416207..55997c87478 100644
--- a/packages/platform-core/API.md
+++ b/packages/platform-core/API.md
@@ -5,10 +5,22 @@
```ts
import { AppId } from '@aws-amplify/plugin-types';
+import { ApplicationLogLevel } from 'aws-cdk-lib/aws-lambda';
import { BackendIdentifier } from '@aws-amplify/plugin-types';
import { DeepPartialAmplifyGeneratedConfigs } from '@aws-amplify/plugin-types';
+import { LogLevel } from '@aws-amplify/plugin-types';
+import { LogRetention } from '@aws-amplify/plugin-types';
+import { RetentionDays } from 'aws-cdk-lib/aws-logs';
import z from 'zod';
+declare namespace __export__cdk {
+ export {
+ LogLevelConverter,
+ LogRetentionConverter
+ }
+}
+export { __export__cdk }
+
// @public
export abstract class AmplifyError extends Error {
constructor(name: T, classification: AmplifyErrorClassification, options: AmplifyErrorOptions, cause?: Error | undefined);
@@ -121,6 +133,18 @@ export class FilePathExtractor {
// @public (undocumented)
export type LocalConfigurationFileName = 'usage_data_preferences.json';
+// @public
+class LogLevelConverter {
+ // (undocumented)
+ toCDKLambdaApplicationLogLevel: (logLevel: LogLevel | undefined) => ApplicationLogLevel | undefined;
+}
+
+// @public
+class LogRetentionConverter {
+ // (undocumented)
+ toCDKRetentionDays: (retention: LogRetention | undefined) => RetentionDays | undefined;
+}
+
// @public
export class ObjectAccumulator {
constructor(accumulator: DeepPartialAmplifyGeneratedConfigs, versionKey?: string);
diff --git a/packages/platform-core/api-extractor.json b/packages/platform-core/api-extractor.json
index 0f56de03f66..cc2ebea8cf9 100644
--- a/packages/platform-core/api-extractor.json
+++ b/packages/platform-core/api-extractor.json
@@ -1,3 +1,4 @@
{
- "extends": "../../api-extractor.base.json"
+ "extends": "../../api-extractor.base.json",
+ "mainEntryPointFilePath": "/lib/index.internal.d.ts"
}
diff --git a/packages/platform-core/package.json b/packages/platform-core/package.json
index 890f108a4b2..131980b90a2 100644
--- a/packages/platform-core/package.json
+++ b/packages/platform-core/package.json
@@ -10,6 +10,11 @@
"types": "./lib/index.d.ts",
"import": "./lib/index.js",
"require": "./lib/index.js"
+ },
+ "./cdk": {
+ "types": "./lib/cdk/index.d.ts",
+ "import": "./lib/cdk/index.js",
+ "require": "./lib/cdk/index.js"
}
},
"main": "lib/index.js",
@@ -31,5 +36,9 @@
"semver": "^7.6.3",
"uuid": "^9.0.1",
"zod": "^3.22.2"
+ },
+ "peerDependencies": {
+ "aws-cdk-lib": "^2.168.0",
+ "constructs": "^10.0.0"
}
}
diff --git a/packages/platform-core/src/.eslintrc.json b/packages/platform-core/src/.eslintrc.json
new file mode 100644
index 00000000000..0da8b97bbb6
--- /dev/null
+++ b/packages/platform-core/src/.eslintrc.json
@@ -0,0 +1,15 @@
+{
+ "rules": {
+ "no-restricted-imports": [
+ "error",
+ {
+ "patterns": [
+ {
+ "group": ["aws-cdk-lib", "aws-cdk-lib/*", "constructs"],
+ "message": "Usage of CDK lib is not allowed in platform-core. Except /cdk entry point. This is to ensure that we don't load CDK eagerly from package root."
+ }
+ ]
+ }
+ ]
+ }
+}
diff --git a/packages/platform-core/src/cdk/.eslintrc.json b/packages/platform-core/src/cdk/.eslintrc.json
new file mode 100644
index 00000000000..c3220a910c1
--- /dev/null
+++ b/packages/platform-core/src/cdk/.eslintrc.json
@@ -0,0 +1,5 @@
+{
+ "rules": {
+ "no-restricted-imports": "off"
+ }
+}
diff --git a/packages/platform-core/src/cdk/enum_converters.test.ts b/packages/platform-core/src/cdk/enum_converters.test.ts
new file mode 100644
index 00000000000..19edc0d1441
--- /dev/null
+++ b/packages/platform-core/src/cdk/enum_converters.test.ts
@@ -0,0 +1,162 @@
+import { describe, it } from 'node:test';
+import assert from 'node:assert';
+import { LogLevel, LogRetention } from '@aws-amplify/plugin-types';
+import { RetentionDays } from 'aws-cdk-lib/aws-logs';
+import { LogLevelConverter, LogRetentionConverter } from './enum_converters';
+import { ApplicationLogLevel } from 'aws-cdk-lib/aws-lambda';
+
+type TestCase = {
+ input: TSource | undefined;
+ expectedOutput: TTarget | undefined;
+};
+
+void describe('LogRetentionConverter', () => {
+ const testCases: Array> = [
+ {
+ input: undefined,
+ expectedOutput: undefined,
+ },
+ {
+ input: '1 day',
+ expectedOutput: RetentionDays.ONE_DAY,
+ },
+ {
+ input: '3 days',
+ expectedOutput: RetentionDays.THREE_DAYS,
+ },
+ {
+ input: '5 days',
+ expectedOutput: RetentionDays.FIVE_DAYS,
+ },
+ {
+ input: '1 week',
+ expectedOutput: RetentionDays.ONE_WEEK,
+ },
+ {
+ input: '2 weeks',
+ expectedOutput: RetentionDays.TWO_WEEKS,
+ },
+ {
+ input: '1 month',
+ expectedOutput: RetentionDays.ONE_MONTH,
+ },
+ {
+ input: '2 months',
+ expectedOutput: RetentionDays.TWO_MONTHS,
+ },
+ {
+ input: '3 months',
+ expectedOutput: RetentionDays.THREE_MONTHS,
+ },
+ {
+ input: '4 months',
+ expectedOutput: RetentionDays.FOUR_MONTHS,
+ },
+ {
+ input: '5 months',
+ expectedOutput: RetentionDays.FIVE_MONTHS,
+ },
+ {
+ input: '6 months',
+ expectedOutput: RetentionDays.SIX_MONTHS,
+ },
+ {
+ input: '13 months',
+ expectedOutput: RetentionDays.THIRTEEN_MONTHS,
+ },
+ {
+ input: '18 months',
+ expectedOutput: RetentionDays.EIGHTEEN_MONTHS,
+ },
+ {
+ input: '1 year',
+ expectedOutput: RetentionDays.ONE_YEAR,
+ },
+ {
+ input: '2 years',
+ expectedOutput: RetentionDays.TWO_YEARS,
+ },
+ {
+ input: '3 years',
+ expectedOutput: RetentionDays.THREE_YEARS,
+ },
+ {
+ input: '5 years',
+ expectedOutput: RetentionDays.FIVE_YEARS,
+ },
+ {
+ input: '6 years',
+ expectedOutput: RetentionDays.SIX_YEARS,
+ },
+ {
+ input: '7 years',
+ expectedOutput: RetentionDays.SEVEN_YEARS,
+ },
+ {
+ input: '8 years',
+ expectedOutput: RetentionDays.EIGHT_YEARS,
+ },
+ {
+ input: '9 years',
+ expectedOutput: RetentionDays.NINE_YEARS,
+ },
+ {
+ input: '10 years',
+ expectedOutput: RetentionDays.TEN_YEARS,
+ },
+ {
+ input: 'infinite',
+ expectedOutput: RetentionDays.INFINITE,
+ },
+ ];
+
+ testCases.forEach((testCase, index) => {
+ void it(`converts log retention[${index}]`, () => {
+ const convertedValue = new LogRetentionConverter().toCDKRetentionDays(
+ testCase.input
+ );
+ assert.strictEqual(convertedValue, testCase.expectedOutput);
+ });
+ });
+});
+
+void describe('LogLevelConverter', () => {
+ const testCases: Array> = [
+ {
+ input: undefined,
+ expectedOutput: undefined,
+ },
+ {
+ input: 'info',
+ expectedOutput: ApplicationLogLevel.INFO,
+ },
+ {
+ input: 'debug',
+ expectedOutput: ApplicationLogLevel.DEBUG,
+ },
+ {
+ input: 'error',
+ expectedOutput: ApplicationLogLevel.ERROR,
+ },
+ {
+ input: 'warn',
+ expectedOutput: ApplicationLogLevel.WARN,
+ },
+ {
+ input: 'trace',
+ expectedOutput: ApplicationLogLevel.TRACE,
+ },
+ {
+ input: 'fatal',
+ expectedOutput: ApplicationLogLevel.FATAL,
+ },
+ ];
+
+ testCases.forEach((testCase, index) => {
+ void it(`converts log retention[${index}]`, () => {
+ const convertedValue =
+ new LogLevelConverter().toCDKLambdaApplicationLogLevel(testCase.input);
+ assert.strictEqual(convertedValue, testCase.expectedOutput);
+ });
+ });
+});
diff --git a/packages/platform-core/src/cdk/enum_converters.ts b/packages/platform-core/src/cdk/enum_converters.ts
new file mode 100644
index 00000000000..3f467785a0c
--- /dev/null
+++ b/packages/platform-core/src/cdk/enum_converters.ts
@@ -0,0 +1,97 @@
+import { LogLevel, LogRetention } from '@aws-amplify/plugin-types';
+import { ApplicationLogLevel } from 'aws-cdk-lib/aws-lambda';
+import { RetentionDays } from 'aws-cdk-lib/aws-logs';
+
+/**
+ * Converts LogRetention to CDK types.
+ */
+export class LogRetentionConverter {
+ toCDKRetentionDays = (
+ retention: LogRetention | undefined
+ ): RetentionDays | undefined => {
+ switch (retention) {
+ case undefined:
+ return undefined;
+
+ case '1 day':
+ return RetentionDays.ONE_DAY;
+ case '3 days':
+ return RetentionDays.THREE_DAYS;
+ case '5 days':
+ return RetentionDays.FIVE_DAYS;
+ case '1 week':
+ return RetentionDays.ONE_WEEK;
+ case '2 weeks':
+ return RetentionDays.TWO_WEEKS;
+ case '1 month':
+ return RetentionDays.ONE_MONTH;
+ case '2 months':
+ return RetentionDays.TWO_MONTHS;
+ case '3 months':
+ return RetentionDays.THREE_MONTHS;
+ case '4 months':
+ return RetentionDays.FOUR_MONTHS;
+ case '5 months':
+ return RetentionDays.FIVE_MONTHS;
+ case '6 months':
+ return RetentionDays.SIX_MONTHS;
+ case '1 year':
+ return RetentionDays.ONE_YEAR;
+ case '13 months':
+ return RetentionDays.THIRTEEN_MONTHS;
+ case '18 months':
+ return RetentionDays.EIGHTEEN_MONTHS;
+ case '2 years':
+ return RetentionDays.TWO_YEARS;
+ case '3 years':
+ return RetentionDays.THREE_YEARS;
+ case '5 years':
+ return RetentionDays.FIVE_YEARS;
+ case '6 years':
+ return RetentionDays.SIX_YEARS;
+ case '7 years':
+ return RetentionDays.SEVEN_YEARS;
+ case '8 years':
+ return RetentionDays.EIGHT_YEARS;
+ case '9 years':
+ return RetentionDays.NINE_YEARS;
+ case '10 years':
+ return RetentionDays.TEN_YEARS;
+ case 'infinite':
+ return RetentionDays.INFINITE;
+ }
+ };
+}
+
+/**
+ * Converts LogLevel to CDK types.
+ */
+export class LogLevelConverter {
+ toCDKLambdaApplicationLogLevel = (
+ logLevel: LogLevel | undefined
+ ): ApplicationLogLevel | undefined => {
+ switch (logLevel) {
+ case undefined: {
+ return undefined;
+ }
+ case 'info': {
+ return ApplicationLogLevel.INFO;
+ }
+ case 'debug': {
+ return ApplicationLogLevel.DEBUG;
+ }
+ case 'warn': {
+ return ApplicationLogLevel.WARN;
+ }
+ case 'error': {
+ return ApplicationLogLevel.ERROR;
+ }
+ case 'fatal': {
+ return ApplicationLogLevel.FATAL;
+ }
+ case 'trace': {
+ return ApplicationLogLevel.TRACE;
+ }
+ }
+ };
+}
diff --git a/packages/platform-core/src/cdk/index.ts b/packages/platform-core/src/cdk/index.ts
new file mode 100644
index 00000000000..d5c9d704c97
--- /dev/null
+++ b/packages/platform-core/src/cdk/index.ts
@@ -0,0 +1,3 @@
+import { LogLevelConverter, LogRetentionConverter } from './enum_converters.js';
+
+export { LogLevelConverter, LogRetentionConverter };
diff --git a/packages/platform-core/src/index.internal.ts b/packages/platform-core/src/index.internal.ts
new file mode 100644
index 00000000000..336a22d74e2
--- /dev/null
+++ b/packages/platform-core/src/index.internal.ts
@@ -0,0 +1,12 @@
+// Suppressing to allow special prefix __export__ that is recognized by API checks.
+// eslint-disable-next-line @typescript-eslint/naming-convention
+import * as __export__cdk from './cdk/index.js';
+
+export * from './index.js';
+
+/*
+ Api-extractor does not ([yet](https://github.com/microsoft/rushstack/issues/1596)) support multiple package entry points
+ Because this package has a submodule export, we are working around this issue by including that export here and directing api-extract to this entry point instead
+ This allows api-extractor to pick up the submodule exports in its analysis
+ */
+export { __export__cdk };
diff --git a/packages/plugin-types/API.md b/packages/plugin-types/API.md
index 2b78dd2ae1c..832ba6c782d 100644
--- a/packages/plugin-types/API.md
+++ b/packages/plugin-types/API.md
@@ -175,6 +175,12 @@ export type ImportPathVerifier = {
verify: (importStack: string | undefined, expectedImportingFile: string, errorMessage: string) => void;
};
+// @public (undocumented)
+export type LogLevel = 'info' | 'debug' | 'warn' | 'error' | 'fatal' | 'trace';
+
+// @public (undocumented)
+export type LogRetention = '1 day' | '3 days' | '5 days' | '1 week' | '2 weeks' | '1 month' | '2 months' | '3 months' | '4 months' | '5 months' | '6 months' | '1 year' | '13 months' | '18 months' | '2 years' | '3 years' | '5 years' | '6 years' | '7 years' | '8 years' | '9 years' | '10 years' | 'infinite';
+
// @public
export type MainStackCreator = {
getOrCreateMainStack: () => Stack;
diff --git a/packages/plugin-types/src/index.ts b/packages/plugin-types/src/index.ts
index 780c52ec02c..8d738f7b2ff 100644
--- a/packages/plugin-types/src/index.ts
+++ b/packages/plugin-types/src/index.ts
@@ -22,3 +22,5 @@ export * from './resource_name_validator.js';
export * from './aws_client_provider.js';
export * from './stack_provider.js';
export * from './amplify_resource_group_name.js';
+export * from './log_level.js';
+export * from './log_retention.js';
diff --git a/packages/plugin-types/src/log_level.ts b/packages/plugin-types/src/log_level.ts
new file mode 100644
index 00000000000..5f4d23c0087
--- /dev/null
+++ b/packages/plugin-types/src/log_level.ts
@@ -0,0 +1 @@
+export type LogLevel = 'info' | 'debug' | 'warn' | 'error' | 'fatal' | 'trace';
diff --git a/packages/plugin-types/src/log_retention.ts b/packages/plugin-types/src/log_retention.ts
new file mode 100644
index 00000000000..60210c721ba
--- /dev/null
+++ b/packages/plugin-types/src/log_retention.ts
@@ -0,0 +1,24 @@
+export type LogRetention =
+ | '1 day'
+ | '3 days'
+ | '5 days'
+ | '1 week'
+ | '2 weeks'
+ | '1 month'
+ | '2 months'
+ | '3 months'
+ | '4 months'
+ | '5 months'
+ | '6 months'
+ | '1 year'
+ | '13 months'
+ | '18 months'
+ | '2 years'
+ | '3 years'
+ | '5 years'
+ | '6 years'
+ | '7 years'
+ | '8 years'
+ | '9 years'
+ | '10 years'
+ | 'infinite';