Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(lambda): cloudwatch lambda insights #15439

Merged
merged 37 commits into from
Jul 29, 2021
Merged
Show file tree
Hide file tree
Changes from 29 commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
94b0060
feat(lambda): enable insights for function
flemjame-at-amazon Jul 2, 2021
a0e4ba5
Add lookup by version and region
flemjame-at-amazon Jul 6, 2021
22bcbfc
Add readme
flemjame-at-amazon Jul 6, 2021
f168918
Merge branch 'master' into lambda-insights-integration
flemjame-at-amazon Jul 6, 2021
6f371ee
Review feedback: put lookup table into Facts and RegionInfo
flemjame-at-amazon Jul 20, 2021
e4b859d
Review feedback: actually add the region info
flemjame-at-amazon Jul 20, 2021
833a678
Add link to information source
flemjame-at-amazon Jul 20, 2021
4cad772
Merge branch 'master' into lambda-insights-integration
flemjame-at-amazon Jul 20, 2021
ce06da6
Found a better way to do a parameterized fact, and added tests of the…
flemjame-at-amazon Jul 20, 2021
18ed795
Merge branch 'lambda-insights-integration' of ssh://github.com/flemja…
flemjame-at-amazon Jul 20, 2021
4ee6ccb
Add test using version
flemjame-at-amazon Jul 20, 2021
dd85e04
Remove stray import
flemjame-at-amazon Jul 20, 2021
912ecac
Get the region at synthesis time so the user doesn't have to provide it
flemjame-at-amazon Jul 21, 2021
907e9ae
Create mapping if no region is provided
flemjame-at-amazon Jul 21, 2021
384bbc0
Add integ test which validates the mappings for region agnostic stacks
flemjame-at-amazon Jul 21, 2021
4743f1d
Update readme
flemjame-at-amazon Jul 21, 2021
a71a539
Update readme
flemjame-at-amazon Jul 21, 2021
b9eb71b
Merge branch 'master' into lambda-insights-integration
flemjame-at-amazon Jul 21, 2021
c347324
Merge pull request #51 from aws/master
flemjame-at-amazon Jul 26, 2021
988f304
Fix merge conflict
flemjame-at-amazon Jul 26, 2021
dfbd045
Fix build failure
flemjame-at-amazon Jul 26, 2021
0337e8b
Merge branch 'master' into lambda-insights-integration
flemjame-at-amazon Jul 26, 2021
594621b
Add verification that all regions exist in the map of version->region…
flemjame-at-amazon Jul 26, 2021
99f742f
Add sanity check test for mapping, and use synthesis in all tests
flemjame-at-amazon Jul 26, 2021
3c1dd9c
Use all caps for the default mapping prefix
flemjame-at-amazon Jul 29, 2021
519ca29
Use IResolveContext instead of any
flemjame-at-amazon Jul 29, 2021
a6ca92f
Only verify the layers property, and add some helper test functions
flemjame-at-amazon Jul 29, 2021
81cb64b
Check for specific error message
flemjame-at-amazon Jul 29, 2021
4051f26
Make comments on mapping clearer, and simplify the code per nija-at's…
flemjame-at-amazon Jul 29, 2021
bf4050c
Merge branch 'master' into lambda-insights-integration
flemjame-at-amazon Jul 29, 2021
6c3b4d4
Update packages/@aws-cdk/aws-lambda/package.json
flemjame-at-amazon Jul 29, 2021
b468557
Remove unnecessary return, and add template match to unit test
flemjame-at-amazon Jul 29, 2021
155e315
Update packages/@aws-cdk/aws-lambda/lib/function.ts
flemjame-at-amazon Jul 29, 2021
f570793
Update packages/@aws-cdk/aws-lambda/README.md
flemjame-at-amazon Jul 29, 2021
6486c4c
Merge branch 'master' into lambda-insights-integration
flemjame-at-amazon Jul 29, 2021
4b86822
Fenced code blocks need language
flemjame-at-amazon Jul 29, 2021
a7f09b9
Merge branch 'master' into lambda-insights-integration
mergify[bot] Jul 29, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions packages/@aws-cdk/aws-lambda/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,25 @@ new LayerVersion(this, 'MyLayer', {
});
```

## Lambda Insights
flemjame-at-amazon marked this conversation as resolved.
Show resolved Hide resolved

Lambda functions can be configured to use CloudWatch [Lambda Insights](https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/Lambda-Insights.html)
which provides low-level runtime metrics for a Lambda functions.

```ts
import * as lambda from '@aws-cdk/lambda';

new Function(this, 'MyFunction', {
insightsVersion: lambda.LambdaInsightsVersion.VERSION_1_0_98_0
})

// Can also provide your own arn if a new version comes out
flemjame-at-amazon marked this conversation as resolved.
Show resolved Hide resolved
const layerArn = 'arn:aws:lambda:us-east-1:580247275435:layer:LambdaInsightsExtension:14';
new Function(this, 'MyFunction', {
insightsVersion: lambda.LambdaInsightsVersion.fromInsightVersionArn(layerArn)
})
```

## Event Rule Target

You can use an AWS Lambda function as a target for an Amazon CloudWatch event
Expand Down
29 changes: 27 additions & 2 deletions packages/@aws-cdk/aws-lambda/lib/function.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,10 @@ import { FileSystem } from './filesystem';
import { FunctionAttributes, FunctionBase, IFunction } from './function-base';
import { calculateFunctionHash, trimFromStart } from './function-hash';
import { Handler } from './handler';
import { LambdaInsightsVersion } from './lambda-insights';
import { Version, VersionOptions } from './lambda-version';
import { CfnFunction } from './lambda.generated';
import { ILayerVersion } from './layers';
import { LayerVersion, ILayerVersion } from './layers';
import { Runtime } from './runtime';

// keep this import separate from other imports to reduce chance for merge conflicts with v2-main
Expand Down Expand Up @@ -214,6 +215,14 @@ export interface FunctionOptions extends EventInvokeConfigOptions {
*/
readonly profilingGroup?: IProfilingGroup;

/**
* Enable Lambda Insights
flemjame-at-amazon marked this conversation as resolved.
Show resolved Hide resolved
* @see https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/Lambda-Insights.html
*
* @default - No Lambda Insights
*/
readonly insightsVersion?: LambdaInsightsVersion;

/**
* A list of layers to add to the function's execution environment. You can configure your Lambda function to pull in
* additional code during initialization in the form of layers. Layers are packages of libraries or other dependencies
Expand Down Expand Up @@ -649,7 +658,7 @@ export class Function extends FunctionBase {
zipFile: code.inlineCode,
imageUri: code.image?.imageUri,
},
layers: Lazy.list({ produce: () => this.layers.map(layer => layer.layerVersionArn) }, { omitEmpty: true }),
layers: Lazy.list({ produce: () => this.layers.map(layer => layer.layerVersionArn) }, { omitEmpty: true }), // Evaluated on synthesis
handler: props.handler === Handler.FROM_IMAGE ? undefined : props.handler,
timeout: props.timeout && props.timeout.toSeconds(),
packageType: props.runtime === Runtime.FROM_IMAGE ? 'Image' : undefined,
Expand Down Expand Up @@ -747,6 +756,11 @@ export class Function extends FunctionBase {
});
});
}

// Configure Lambda insights
if (props.insightsVersion !== undefined) {
this.configureLambdaInsights(props.insightsVersion);
}
}

/**
Expand Down Expand Up @@ -863,6 +877,17 @@ Environment variables can be marked for removal when used in Lambda@Edge by sett
return;
}

/**
* Configured lambda insights on the function if specified. This is acheived by adding an imported layer which is added to the
* list of lambda layers on synthesis.
*
* https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/Lambda-Insights-extension-versions.html
*/
private configureLambdaInsights(insightsVersion: LambdaInsightsVersion): void {
this.addLayers(LayerVersion.fromLayerVersionArn(this, 'LambdaInsightsLayer', insightsVersion.layerVersionArn));
this.role?.addManagedPolicy(iam.ManagedPolicy.fromAwsManagedPolicyName('CloudWatchLambdaInsightsExecutionRolePolicy'));
}

private renderEnvironment() {
if (!this.environment || Object.keys(this.environment).length === 0) {
return undefined;
Expand Down
2 changes: 1 addition & 1 deletion packages/@aws-cdk/aws-lambda/lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export * from './destination';
export * from './event-invoke-config';
export * from './scalable-attribute-api';
export * from './code-signing-config';

export * from './lambda-insights';
export * from './log-retention';

// AWS::Lambda CloudFormation Resources:
Expand Down
134 changes: 134 additions & 0 deletions packages/@aws-cdk/aws-lambda/lib/lambda-insights.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
import { Aws, CfnMapping, Fn, IResolveContext, Lazy, Stack, Token } from '@aws-cdk/core';
import { FactName, RegionInfo } from '@aws-cdk/region-info';
import { CLOUDWATCH_LAMBDA_INSIGHTS_ARNS } from '@aws-cdk/region-info/build-tools/fact-tables';

// This is the name of the mapping that will be added to the CloudFormation template, if a stack is region agnostic
const DEFAULT_MAPPING_PREFIX = 'LambdaInsightsVersions';

// To add new versions, update fact-tables.ts `CLOUDWATCH_LAMBDA_INSIGHTS_ARNS` and create a new `public static readonly VERSION_A_B_C_D`

/**
* Version of CloudWatch Lambda Insights
*/
export abstract class LambdaInsightsVersion {

/**
* Version 1.0.54.0
*/
public static readonly VERSION_1_0_54_0 = LambdaInsightsVersion.fromInsightsVersion('1.0.54.0');

/**
* Version 1.0.86.0
*/
public static readonly VERSION_1_0_86_0 = LambdaInsightsVersion.fromInsightsVersion('1.0.86.0');

/**
* Version 1.0.89.0
*/
public static readonly VERSION_1_0_89_0 = LambdaInsightsVersion.fromInsightsVersion('1.0.89.0');

/**
* Version 1.0.98.0
*/
public static readonly VERSION_1_0_98_0 = LambdaInsightsVersion.fromInsightsVersion('1.0.98.0');

/**
* Use the insights extension associated with the provided ARN. Make sure the ARN is associated
* with same region as your function
*
* @see https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/Lambda-Insights-extension-versions.html
*/
public static fromInsightVersionArn(arn: string): LambdaInsightsVersion {
class InsightsArn extends LambdaInsightsVersion {
public readonly layerVersionArn = arn;
}
return new InsightsArn();
}

// Use the verison to build the object. Not meant to be called by the user -- user should use e.g. VERSION_1_0_54_0
private static fromInsightsVersion(insightsVersion: string): LambdaInsightsVersion {

// Check if insights version is valid. This should only happen if one of the public static readonly versions are set incorrectly
if (!(insightsVersion in CLOUDWATCH_LAMBDA_INSIGHTS_ARNS)) {
throw new Error(`Insights version ${insightsVersion} does not exist. Available versions are ${CLOUDWATCH_LAMBDA_INSIGHTS_ARNS.keys()}`);
}

class InsightsVersion extends LambdaInsightsVersion {
public readonly layerVersionArn = Lazy.uncachedString({
produce: (context) => getVersionArn(context, insightsVersion),
});
}
return new InsightsVersion();
}

/**
* The arn of the Lambda Insights extension
*/
public readonly layerVersionArn: string = '';
}

/**
* Function to retrieve the correct Lambda Insights ARN from RegionInfo,
* or create a mapping to look it up at stack deployment time.
*
* This function is run on CDK synthesis.
*/
function getVersionArn(context: IResolveContext, insightsVersion: string): string {

const scopeStack = Stack.of(context.scope);
const region = scopeStack.region;

// Region is defined, look up the arn, or throw an error if the version isn't supported by a region
if (region !== undefined && !Token.isUnresolved(region)) {
const arn = RegionInfo.get(region).cloudwatchLambdaInsightsArn(insightsVersion);
if (arn === undefined) {
throw new Error(`Insights version ${insightsVersion} is not supported in region ${region}`);
}
return arn;
}

// Otherwise, need to add a mapping to be looked up at deployment time

/**
* See this for the context as to why the mappings are the way they are
* https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/mappings-section-structure.html
*
* Mappings have to have a structure like this, and no functions can be used inside them:
* <Alphanumeric only>
* - <Can be non-alphanumeric>
* -- { <alphanumeric>: "value1"},
* -- { <alphanumeric>: "value2"}
*
* So we cannot have an otherwise ideal mapping like this, because '1.0.98.0' is non-alphanumeric:
* LambdaInsightsVersions
* - us-east-1
* -- {'1.0.98.0': 'arn1'},
* -- {'1.0.89.0': 'arn2'}
*
* To get around this limitation, this is the mapping structure:
* LambdaInsightsVersions10980 // for version 1.0.98.0
* - us-east-1
* -- {'arn': 'arn1'},
* - us-east-2
* -- {'arn': 'arn2'}
* LambdaInsightsVersions10890 // a separate mapping version 1.0.89.0
* - us-east-1
* -- {'arn': 'arn3'},
* - us-east-2
* -- {'arn': 'arn4'}
*/

const mapName = DEFAULT_MAPPING_PREFIX + insightsVersion.split('.').join('');
const mapping: { [k1: string]: { [k2: string]: any } } = {};
const region2arns = RegionInfo.regionMap(FactName.cloudwatchLambdaInsightsVersion(insightsVersion));
for (const [reg, arn] of Object.entries(region2arns)) {
mapping[reg] = { arn };
}

// Only create a given mapping once. If another version of insights is used elsewhere, that mapping will also exist
if (!scopeStack.node.tryFindChild(mapName)) {
new CfnMapping(scopeStack, mapName, { mapping });
}
// The ARN will be looked up at deployment time from the mapping we created
return Fn.findInMap(mapName, Aws.REGION, 'arn');
}
4 changes: 3 additions & 1 deletion packages/@aws-cdk/aws-lambda/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@
"@aws-cdk/aws-sqs": "0.0.0",
"@aws-cdk/core": "0.0.0",
"@aws-cdk/cx-api": "0.0.0",
"@aws-cdk/region-info": "0.0.0",
"constructs": "^3.3.69"
},
"homepage": "https://github.com/aws/aws-cdk",
Expand All @@ -127,7 +128,8 @@
"@aws-cdk/aws-sqs": "0.0.0",
"@aws-cdk/core": "0.0.0",
"@aws-cdk/cx-api": "0.0.0",
"constructs": "^3.3.69"
"constructs": "^3.3.69",
"@aws-cdk/region-info": "0.0.0"
flemjame-at-amazon marked this conversation as resolved.
Show resolved Hide resolved
},
"engines": {
"node": ">= 10.13.0 <13 || >=13.7.0"
Expand Down
Loading