├── .eslintrc.json ├── .gitignore ├── .npmignore ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── example ├── .gitignore ├── handler.js └── serverless.yml ├── index.js ├── index.test.js ├── layerVersions.json ├── layerVersionsArm64.json ├── package-lock.json └── package.json /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "es2021": true 5 | }, 6 | "extends": ["google"], 7 | "parserOptions": { 8 | "ecmaVersion": 12, 9 | "sourceType": "module" 10 | }, 11 | "rules": { 12 | "max-len": ["error", { "code": 120 }] 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | # Diagnostic reports (https://nodejs.org/api/report.html) 10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | *.lcov 24 | 25 | # nyc test coverage 26 | .nyc_output 27 | 28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 29 | .grunt 30 | 31 | # Bower dependency directory (https://bower.io/) 32 | bower_components 33 | 34 | # node-waf configuration 35 | .lock-wscript 36 | 37 | # Compiled binary addons (https://nodejs.org/api/addons.html) 38 | build/Release 39 | 40 | # Dependency directories 41 | node_modules/ 42 | jspm_packages/ 43 | 44 | # TypeScript v1 declaration files 45 | typings/ 46 | 47 | # TypeScript cache 48 | *.tsbuildinfo 49 | 50 | # Optional npm cache directory 51 | .npm 52 | 53 | # Optional eslint cache 54 | .eslintcache 55 | 56 | # Microbundle cache 57 | .rpt2_cache/ 58 | .rts2_cache_cjs/ 59 | .rts2_cache_es/ 60 | .rts2_cache_umd/ 61 | 62 | # Optional REPL history 63 | .node_repl_history 64 | 65 | # Output of 'npm pack' 66 | *.tgz 67 | 68 | # Yarn Integrity file 69 | .yarn-integrity 70 | 71 | # dotenv environment variables file 72 | .env 73 | .env.test 74 | 75 | # parcel-bundler cache (https://parceljs.org/) 76 | .cache 77 | 78 | # Next.js build output 79 | .next 80 | 81 | # Nuxt.js build / generate output 82 | .nuxt 83 | dist 84 | 85 | # Gatsby files 86 | .cache/ 87 | # Comment in the public line in if your project uses Gatsby and *not* Next.js 88 | # https://nextjs.org/blog/next-9-1#public-directory-support 89 | # public 90 | 91 | # vuepress build output 92 | .vuepress/dist 93 | 94 | # Serverless directories 95 | .serverless/ 96 | 97 | # FuseBox cache 98 | .fusebox/ 99 | 100 | # DynamoDB Local files 101 | .dynamodb/ 102 | 103 | # TernJS port file 104 | .tern-port 105 | 106 | # Other 107 | .DS_Store 108 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | test/ 2 | coverage/ 3 | example/ 4 | .editorconfig 5 | .eslintignore 6 | .eslintrc 7 | .eslintrc.json 8 | .github 9 | .gitignore 10 | .npmignore 11 | .travis.yml 12 | 13 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | All changes to this project will be documented in this file. 3 | 4 | ## [2.0.0] 2023-04-08 5 | 6 | ### Changed 7 | - Changed all Lambda Insights layer versions to the latest. 8 | ### Added 9 | 10 | ### Fixed 11 | - Selecting the right layer version if provider-level architecture is different from function-level architecture. This fix might change the Lambda Layer version used in your functions. 12 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | ## Code of Conduct 2 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). 3 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact 4 | opensource-codeofconduct@amazon.com with any additional questions or comments. 5 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing Guidelines 2 | 3 | Thank you for your interest in contributing to our project. Whether it's a bug report, new feature, correction, or additional 4 | documentation, we greatly value feedback and contributions from our community. 5 | 6 | Please read through this document before submitting any issues or pull requests to ensure we have all the necessary 7 | information to effectively respond to your bug report or contribution. 8 | 9 | 10 | ## Reporting Bugs/Feature Requests 11 | 12 | We welcome you to use the GitHub issue tracker to report bugs or suggest features. 13 | 14 | When filing an issue, please check existing open, or recently closed, issues to make sure somebody else hasn't already 15 | reported the issue. Please try to include as much information as you can. Details like these are incredibly useful: 16 | 17 | * A reproducible test case or series of steps 18 | * The version of our code being used 19 | * Any modifications you've made relevant to the bug 20 | * Anything unusual about your environment or deployment 21 | 22 | 23 | ## Contributing via Pull Requests 24 | Contributions via pull requests are much appreciated. Before sending us a pull request, please ensure that: 25 | 26 | 1. You are working against the latest source on the *main* branch. 27 | 2. You check existing open, and recently merged, pull requests to make sure someone else hasn't addressed the problem already. 28 | 3. You open an issue to discuss any significant work - we would hate for your time to be wasted. 29 | 30 | To send us a pull request, please: 31 | 32 | 1. Fork the repository. 33 | 2. Modify the source; please focus on the specific change you are contributing. If you also reformat all the code, it will be hard for us to focus on your change. 34 | 3. Ensure local tests pass. 35 | 4. Commit to your fork using clear commit messages. 36 | 5. Send us a pull request, answering any default questions in the pull request interface. 37 | 6. Pay attention to any automated CI failures reported in the pull request, and stay involved in the conversation. 38 | 39 | GitHub provides additional document on [forking a repository](https://help.github.com/articles/fork-a-repo/) and 40 | [creating a pull request](https://help.github.com/articles/creating-a-pull-request/). 41 | 42 | 43 | ## Finding contributions to work on 44 | Looking at the existing issues is a great way to find something to contribute on. As our projects, by default, use the default GitHub issue labels (enhancement/bug/duplicate/help wanted/invalid/question/wontfix), looking at any 'help wanted' issues is a great place to start. 45 | 46 | 47 | ## Code of Conduct 48 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). 49 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact 50 | opensource-codeofconduct@amazon.com with any additional questions or comments. 51 | 52 | 53 | ## Security issue notifications 54 | If you discover a potential security issue in this project we ask that you notify AWS/Amazon Security via our [vulnerability reporting page](http://aws.amazon.com/security/vulnerability-reporting/). Please do **not** create a public github issue. 55 | 56 | 57 | ## Licensing 58 | 59 | See the [LICENSE](LICENSE) file for our project's licensing. We will ask you to confirm the licensing of your contribution. 60 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT-0 License 2 | 3 | Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so. 10 | 11 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 12 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 13 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 14 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 15 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 16 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 17 | 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # serverless-plugin-lambda-insights 2 | 3 | A Serverless Framework Plugin allowing to enable Lambda Insights 4 | 5 | ![npm](https://img.shields.io/npm/v/serverless-plugin-lambda-insights) 6 | ![npm](https://img.shields.io/npm/dw/serverless-plugin-lambda-insights) 7 | 8 | Enables AWS Lambda Insights (https://aws.amazon.com/blogs/mt/introducing-cloudwatch-lambda-insights/) for the entire Serverless stack functions or individual functions. 9 | 10 | ## Why use Lambda Insights 11 | 12 | > _CloudWatch Lambda Insights_ is a monitoring and troubleshooting solution for serverless applications running on AWS Lambda. The solution collects, aggregates, and summarizes system-level metrics including CPU time, memory, disk, and network. It also collects, aggregates, and summarizes diagnostic information such as cold starts and Lambda worker shutdowns to help you isolate issues with your Lambda functions and resolve them quickly. 13 | 14 | ![AWS Documentation Example](https://docs.aws.amazon.com/lambda/latest/dg/images/lambdainsights-multifunction-view.png) 15 | 16 | --- 17 | 18 | ## Getting started 19 | 20 | ### Installation 21 | 22 | This Plugin requires a Serverless Framework version of >= 2.0.0. 23 | 24 | `npm install --save-dev serverless-plugin-lambda-insights` 25 | 26 | add Plugin to your `serverless.yml` in the plugins section. 27 | 28 | ### Minimal Usage 29 | 30 | Example `serverless.yml`: 31 | 32 | ```yaml 33 | provider: 34 | name: aws 35 | 36 | plugins: 37 | - serverless-plugin-lambda-insights 38 | 39 | functions: 40 | hello: 41 | handler: handler.hello 42 | lambdaInsights: true 43 | ``` 44 | 45 | ### Functionality 46 | 47 | The plugin will enable Lambda Insights by adding a Lambda Layer ([see Layer Details and Versions](https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/Lambda-Insights-extension-versions.html)) and adding necessary permissions using the `arn:aws:iam::aws:policy/CloudWatchLambdaInsightsExecutionRolePolicy` as a AWS IAM Managed Policy. 48 | 49 | You can check in your AWS Console: 50 | go to AWS Lambda -> select your Lambda function -> Configuration tab -> Monitoring tools -> 51 | "CloudWatch Lambda Insights". 52 | If `lambdaInsights` validated to `true` for a function, 53 | the checkbox will be checked. 54 | 55 | ### Usage 56 | 57 | Example `serverless.yml`: 58 | 59 | ```yaml 60 | service: your-great-sls-service 61 | 62 | provider: 63 | name: aws 64 | stage: dev 65 | 66 | plugins: 67 | - serverless-plugin-lambda-insights 68 | 69 | functions: 70 | mainFunction: #inherits tracing settings from "provider" 71 | lambdaInsights: true #enables Lambda Insights for this function 72 | handler: src/app/index.handler 73 | secondFunction: #inherits tracing settings from "provider" 74 | lambdaInsights: false #disables Lambda Insights for this function, will overrule custom settings 75 | handler: src/app/index.handler 76 | 77 | custom: 78 | lambdaInsights: 79 | defaultLambdaInsights: true #enables Lambda Insights for all your functions, if 80 | attachPolicy: false #explicitly disable auto attachment Managed Policy. 81 | lambdaInsightsVersion: 14 #specify the Layer Version 82 | ``` 83 | 84 | ### Example 85 | 86 | You can find an example in the example folder of this repository. Run it with the following command. 87 | 88 | `cd example; serverless deploy` 89 | 90 | This will deploy a hello-world Lambda function with Lambda Insights enabled. 91 | 92 | --- 93 | 94 | ## Want to contribute? 95 | 96 | This is your repo - just go head and create a pull request. See also [CONTRIBUTING](CONTRIBUTING.md) for more introductions. 97 | 98 | Some open Ideas and Tasks: 99 | 100 | - [x] Testing with Jest 101 | - [x] Add Toggle for auto policy attachment 102 | - [x] Add an example 103 | 104 | --- 105 | 106 | ## Security 107 | 108 | See [CONTRIBUTING](CONTRIBUTING.md#security-issue-notifications) for more information. 109 | 110 | ## License 111 | 112 | This library is licensed under the MIT-0 License. See the [LICENSE](LICENSE) file. 113 | -------------------------------------------------------------------------------- /example/.gitignore: -------------------------------------------------------------------------------- 1 | # package directories 2 | node_modules 3 | jspm_packages 4 | 5 | # Serverless directories 6 | .serverless -------------------------------------------------------------------------------- /example/handler.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports.hello = async (event) => { 4 | return { 5 | statusCode: 200, 6 | body: JSON.stringify( 7 | { 8 | message: 'Go Serverless v1.0! Your function executed successfully!', 9 | input: event, 10 | }, 11 | null, 12 | 2, 13 | ), 14 | }; 15 | 16 | // Use this code if you don't use the http event with the LAMBDA-PROXY integration 17 | // return { message: 'Go Serverless v1.0! Your function executed successfully!', event }; 18 | }; 19 | -------------------------------------------------------------------------------- /example/serverless.yml: -------------------------------------------------------------------------------- 1 | service: testService 2 | 3 | frameworkVersion: '3' 4 | 5 | plugins: 6 | - ./../index.js 7 | 8 | provider: 9 | name: aws 10 | runtime: nodejs12.x 11 | 12 | functions: 13 | hello: 14 | handler: handler.hello 15 | lambdaInsights: true 16 | architecture: x86_64 17 | 18 | custom: 19 | lambdaInsights: 20 | lambdaInsightsVersion: 35 #specify the Layer Version -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // Lambda Insight Layer Versions 4 | // see https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/Lambda-Insights-extension-versions.html 5 | const layerVersions = require('./layerVersions.json'); 6 | const layerVersionsArm64 = require('./layerVersionsArm64.json'); 7 | 8 | const lambdaInsightsManagedPolicy = 'arn:aws:iam::aws:policy/CloudWatchLambdaInsightsExecutionRolePolicy'; 9 | 10 | /** 11 | * Serverless Lambda Insights Plugin - serverless-plugin-lambda-insights 12 | * @class AddLambdaInsights 13 | */ 14 | class AddLambdaInsights { 15 | /** 16 | * AddLambdaInsights constructor 17 | * This class gets instantiated with a serverless object and a bunch of options. 18 | * @param {object} serverless The serverless instance which enables access to global service config during 19 | */ 20 | constructor(serverless) { 21 | this.serverless = serverless; 22 | this.service = this.serverless.service; 23 | this.provider = this.serverless.getProvider('aws'); 24 | this.hooks = { 25 | 'before:package:setupProviderConfiguration': this.addLambdaInsights.bind(this), 26 | }; 27 | if (this.checkValidationSupport(serverless)) { 28 | serverless.configSchemaHandler.defineFunctionProperties('aws', { 29 | properties: { 30 | lambdaInsights: {type: 'boolean'}, 31 | }, 32 | }); 33 | 34 | serverless.configSchemaHandler.defineCustomProperties({ 35 | properties: { 36 | lambdaInsights: { 37 | type: 'object', 38 | properties: { 39 | defaultLambdaInsights: {type: 'boolean'}, 40 | attachPolicy: {type: 'boolean'}, 41 | lambdaInsightsVersion: {type: 'number'}, 42 | } 43 | }, 44 | }, 45 | }); 46 | } 47 | } 48 | 49 | /** 50 | * Validates serverless object has required validation fields 51 | * @param {any} serverless 52 | * @return {boolean} Whether installed serverless fw supports validation 53 | */ 54 | checkValidationSupport(serverless) { 55 | return serverless.configSchemaHandler && 56 | serverless.configSchemaHandler.defineFunctionProperties && 57 | typeof serverless.configSchemaHandler['defineFunctionProperties'] == 'function' && 58 | serverless.configSchemaHandler.defineCustomProperties && 59 | typeof serverless.configSchemaHandler['defineCustomProperties'] == 'function'; 60 | } 61 | 62 | /** 63 | * Check if Lambda Insights parameter is of type Boolean 64 | * @param {any} value Value to check 65 | * @return {boolean} return input value if boolean 66 | */ 67 | checkLambdaInsightsType(value) { 68 | if (typeof value === 'boolean') { 69 | return value; 70 | } else { 71 | throw new Error( 72 | 'LambdaInsights and DefaultLambdaInsights values must be set to either true or false.', 73 | ); 74 | } 75 | } 76 | 77 | /** 78 | * Check if Lambda Insights Layer Version is valid 79 | * @param {any} value Value to check 80 | * @return {boolean} return input value if available 81 | */ 82 | checkLambdaInsightsVersion(value) { 83 | if (typeof value === 'number') { 84 | return value; 85 | } else { 86 | throw new Error('lambdaInsightsVersion version must be a number.'); 87 | } 88 | } 89 | 90 | /** 91 | * Generates a valid Lambda Insights Layer ARN for your Region 92 | * @param {number} version Value to check 93 | * @return {string} Lambda Insights Layer ARN 94 | */ 95 | async generateLayerARN(version, architecture) { 96 | const region = this.provider.getRegion(); 97 | const isArm64 = architecture ? architecture === 'arm64' : this.service.provider.architecture === 'arm64'; 98 | if (version) { 99 | try { 100 | let layerVersionInfo; 101 | if (isArm64) { 102 | layerVersionInfo = await this.provider.request('Lambda', 'getLayerVersionByArn', { 103 | Arn: `arn:aws:lambda:${region}:580247275435:layer:LambdaInsightsExtension-Arm64:${version}`, 104 | }); 105 | } else { 106 | layerVersionInfo = await this.provider.request('Lambda', 'getLayerVersionByArn', { 107 | Arn: `arn:aws:lambda:${region}:580247275435:layer:LambdaInsightsExtension:${version}`, 108 | }); 109 | } 110 | return layerVersionInfo.LayerVersionArn; 111 | } catch (err) { 112 | if (err.code==='AccessDeniedException') { 113 | throw new Error( 114 | `LambdaInsights layer version '${version}' ` + 115 | `does not exist within your region '${region}'.`); 116 | } else { 117 | throw err; 118 | } 119 | } 120 | } 121 | 122 | let arn; 123 | if (isArm64) { 124 | arn = layerVersionsArm64[region]; 125 | } else { 126 | arn = layerVersions[region]; 127 | } 128 | if (!arn) { 129 | throw new Error( 130 | `Unknown latest version for region '${region}'. ` + 131 | `Check the Lambda Insights documentation to get the list of currently supported versions.`); 132 | } 133 | return arn; 134 | }; 135 | 136 | /** 137 | * Attach Lambda Layer conditionally to each function 138 | * @param {boolean} globalLambdaInsights global settings 139 | * @param {number} layerVersion global layerVersion settings 140 | * @param {boolean} attachPolicy global attachPolicy settings 141 | */ 142 | async addLambdaInsightsToFunctions(globalLambdaInsights, layerVersion, attachPolicy) { 143 | if (typeof this.service.functions !== 'object') { 144 | return; 145 | } 146 | try { 147 | 148 | let policyToggle = false; 149 | const functions = Object.keys(this.service.functions); 150 | for (const functionName of functions) { 151 | const fn = this.service.functions[functionName]; 152 | const layerARN = await this.generateLayerARN(layerVersion, fn.architecture); 153 | const localLambdaInsights = fn.hasOwnProperty('lambdaInsights') ? 154 | this.checkLambdaInsightsType(fn.lambdaInsights) : 155 | null; 156 | 157 | if ( 158 | localLambdaInsights === false || 159 | (localLambdaInsights === null && globalLambdaInsights === null) 160 | ) { 161 | continue; 162 | } 163 | 164 | const fnLambdaInsights = localLambdaInsights || globalLambdaInsights; 165 | 166 | if (fnLambdaInsights) { 167 | // attach Lambda Layer 168 | fn.layers = fn.layers || []; 169 | fn.layers.push(layerARN); 170 | policyToggle = true; 171 | } 172 | }; 173 | if (attachPolicy && policyToggle) { 174 | // attach CloudWatchLambdaInsightsExecutionRolePolicy 175 | this.service.provider.iamManagedPolicies = 176 | this.service.provider.iamManagedPolicies || []; 177 | this.service.provider.iamManagedPolicies.push(lambdaInsightsManagedPolicy); 178 | } 179 | return; 180 | } catch (err) { 181 | throw err; 182 | } 183 | } 184 | 185 | /** 186 | * Hook function to get global config value and executes addLambdaInsightsToFunctions 187 | * @return {Promise} Lambda Insights Layer ARN 188 | */ 189 | addLambdaInsights() { 190 | const customLambdaInsights = 191 | this.service.custom && this.service.custom.lambdaInsights; 192 | 193 | const globalLambdaInsights = 194 | customLambdaInsights && customLambdaInsights.defaultLambdaInsights ? 195 | this.checkLambdaInsightsType( 196 | customLambdaInsights.defaultLambdaInsights, 197 | ) : 198 | null; 199 | 200 | const attachPolicy = 201 | customLambdaInsights && customLambdaInsights.hasOwnProperty('attachPolicy') ? 202 | this.checkLambdaInsightsType( 203 | customLambdaInsights.attachPolicy, 204 | ) : 205 | true; 206 | 207 | const layerVersion = 208 | customLambdaInsights && customLambdaInsights.lambdaInsightsVersion ? 209 | this.checkLambdaInsightsVersion( 210 | customLambdaInsights.lambdaInsightsVersion, 211 | ) : 212 | null; 213 | 214 | return this.addLambdaInsightsToFunctions(globalLambdaInsights, layerVersion, attachPolicy); 215 | } 216 | } 217 | 218 | module.exports = AddLambdaInsights; 219 | -------------------------------------------------------------------------------- /index.test.js: -------------------------------------------------------------------------------- 1 | const AddLambdaInsights = require('./index'); 2 | 3 | test('addLambdaInsights associates latest ARN', async () => { 4 | // arrange 5 | const serverless = createServerless('us-east-1'); 6 | const plugin = new AddLambdaInsights(serverless); 7 | 8 | // act 9 | await plugin.addLambdaInsights(); 10 | 11 | // assert 12 | expect(plugin.serverless.service.functions.myFunction.layers) 13 | .toStrictEqual(['arn:aws:lambda:us-east-1:580247275435:layer:LambdaInsightsExtension:35']); 14 | }); 15 | 16 | test('generateLayerArn defaults to global provider architecture to associates latest ARN for Arm64', async () => { 17 | // arrange 18 | const serverless = createServerless('us-east-1'); 19 | serverless.service.provider.architecture = 'arm64'; 20 | const plugin = new AddLambdaInsights(serverless); 21 | 22 | // act 23 | await plugin.addLambdaInsights(); 24 | 25 | // assert 26 | expect(plugin.serverless.service.functions.myFunction.layers) 27 | .toStrictEqual(['arn:aws:lambda:us-east-1:580247275435:layer:LambdaInsightsExtension-Arm64:2']); 28 | }); 29 | 30 | test('generateLayerArn local function architecture overwrites global setting to associates latest ARN for Arm64', async () => { 31 | // arrange 32 | const serverless = createServerless('us-east-1'); 33 | serverless.service.functions.myFunction.architecture = 'arm64'; 34 | const plugin = new AddLambdaInsights(serverless); 35 | 36 | // act 37 | await plugin.addLambdaInsights(); 38 | 39 | // assert 40 | expect(plugin.serverless.service.functions.myFunction.layers) 41 | .toStrictEqual(['arn:aws:lambda:us-east-1:580247275435:layer:LambdaInsightsExtension-Arm64:2']); 42 | }); 43 | 44 | test('generateLayerArn supports multi architecture setting to associate latest ARN', async () => { 45 | // arrange 46 | const serverless = createServerless('us-east-1'); 47 | serverless.service.functions.myArm64Function = { 48 | lambdaInsights: true, 49 | handler: 'handler.hello', 50 | architecture: 'arm64', 51 | }; 52 | const plugin = new AddLambdaInsights(serverless); 53 | 54 | // act 55 | await plugin.addLambdaInsights(); 56 | 57 | // assert 58 | expect(plugin.serverless.service.functions.myFunction.layers) 59 | .toStrictEqual(['arn:aws:lambda:us-east-1:580247275435:layer:LambdaInsightsExtension:35']); 60 | expect(plugin.serverless.service.functions.myArm64Function.layers) 61 | .toStrictEqual(['arn:aws:lambda:us-east-1:580247275435:layer:LambdaInsightsExtension-Arm64:2']); 62 | }); 63 | 64 | test('addLambdaInsights associates correct explicit layer version', async () => { 65 | // arrange 66 | const serverless = createServerless('us-east-1', 12); 67 | const plugin = new AddLambdaInsights(serverless); 68 | 69 | // act 70 | await plugin.addLambdaInsights(); 71 | 72 | // assert 73 | expect(plugin.serverless.service.functions.myFunction.layers) 74 | .toStrictEqual(['arn:aws:lambda:us-east-1:580247275435:layer:LambdaInsightsExtension:12']); 75 | }); 76 | 77 | test('addLambdaInsights throws for unknown region', async () => { 78 | // arrange 79 | const serverless = createServerless('not-a-region-1'); 80 | const plugin = new AddLambdaInsights(serverless); 81 | 82 | // act 83 | const task = () => plugin.addLambdaInsights(); 84 | 85 | await expect(task).rejects 86 | .toThrow('Unknown latest version for region \'not-a-region-1\'. ' + 87 | 'Check the Lambda Insights documentation to get the list of currently supported versions.'); 88 | }); 89 | 90 | test('addLambdaInsights throws invalid lambdaInsightsVersion argument', async () => { 91 | // arrange 92 | const serverless = createServerless('us-east-1', 'some_wrong_version_number'); 93 | const plugin = new AddLambdaInsights(serverless); 94 | 95 | // act 96 | const task = () => plugin.addLambdaInsights(); 97 | 98 | // assert 99 | await expect(task) 100 | .toThrow('lambdaInsightsVersion version must be a number.'); 101 | }); 102 | 103 | test('addLambdaInsights throws for invalid region version combination', async () => { 104 | // arrange 105 | const serverless = createServerless('us-east-1', 55555); 106 | const plugin = new AddLambdaInsights(serverless); 107 | 108 | // act 109 | const task = () => plugin.addLambdaInsights(); 110 | 111 | // assert 112 | await expect(task).rejects 113 | .toThrow('LambdaInsights layer version \'55555\' does not exist within your region \'us-east-1\'.'); 114 | }); 115 | 116 | 117 | test('addLambdaInsights adds IAM policy', async () => { 118 | // arrange 119 | const serverless = createServerless('us-east-1'); 120 | const plugin = new AddLambdaInsights(serverless); 121 | 122 | // act 123 | await plugin.addLambdaInsights(); 124 | 125 | // assert 126 | expect(plugin.service.provider.iamManagedPolicies) 127 | .toStrictEqual(['arn:aws:iam::aws:policy/CloudWatchLambdaInsightsExecutionRolePolicy']); 128 | }); 129 | 130 | 131 | const createServerless = (region, LayerVersion) => { 132 | const awsProvider = { 133 | getRegion: () => region, 134 | request: async (service, method, param) => { 135 | // explicit layer version test is only valid for version 12 136 | if (param.Arn===`arn:aws:lambda:${region}:580247275435:layer:LambdaInsightsExtension:12`) { 137 | return {LayerVersionArn: param.Arn}; 138 | } else { 139 | const customError = new Error(); 140 | customError.code = 'AccessDeniedException'; 141 | throw customError; 142 | } 143 | }, 144 | }; 145 | return { 146 | getProvider: () => awsProvider, 147 | configSchemaHandler: { 148 | defineFunctionProperties: () => jest.fn(), 149 | defineCustomProperties: () => jest.fn(), 150 | }, 151 | service: { 152 | provider: { 153 | name: 'aws', 154 | runtime: 'nodejs12.x', 155 | architecture: 'x86_64', 156 | }, 157 | custom: { 158 | lambdaInsights: { 159 | lambdaInsightsVersion: LayerVersion, 160 | }, 161 | }, 162 | functions: { 163 | myFunction: { 164 | lambdaInsights: true, 165 | handler: 'handler.hello', 166 | }, 167 | }, 168 | }, 169 | }; 170 | }; 171 | -------------------------------------------------------------------------------- /layerVersions.json: -------------------------------------------------------------------------------- 1 | { 2 | "us-east-1": "arn:aws:lambda:us-east-1:580247275435:layer:LambdaInsightsExtension:35", 3 | "us-east-2": "arn:aws:lambda:us-east-2:580247275435:layer:LambdaInsightsExtension:33", 4 | "us-west-1": "arn:aws:lambda:us-west-1:580247275435:layer:LambdaInsightsExtension:33", 5 | "us-west-2": "arn:aws:lambda:us-west-2:580247275435:layer:LambdaInsightsExtension:33", 6 | "af-south-1": "arn:aws:lambda:af-south-1:012438385374:layer:LambdaInsightsExtension:25", 7 | "ap-east-1": "arn:aws:lambda:ap-east-1:519774774795:layer:LambdaInsightsExtension:25", 8 | "ap-south-2": "arn:aws:lambda:ap-south-2:891564319516:layer:LambdaInsightsExtension:8", 9 | "ap-southeast-3": "arn:aws:lambda:ap-southeast-3:439286490199:layer:LambdaInsightsExtension:11", 10 | "ap-south-1": "arn:aws:lambda:ap-south-1:580247275435:layer:LambdaInsightsExtension:31", 11 | "ap-northeast-3": "arn:aws:lambda:ap-northeast-3:194566237122:layer:LambdaInsightsExtension:2", 12 | "ap-northeast-2": "arn:aws:lambda:ap-northeast-2:580247275435:layer:LambdaInsightsExtension:32", 13 | "ap-southeast-1": "arn:aws:lambda:ap-southeast-1:580247275435:layer:LambdaInsightsExtension:33", 14 | "ap-southeast-2": "arn:aws:lambda:ap-southeast-2:580247275435:layer:LambdaInsightsExtension:33", 15 | "ap-northeast-1": "arn:aws:lambda:ap-northeast-1:580247275435:layer:LambdaInsightsExtension:50", 16 | "ca-central-1": "arn:aws:lambda:ca-central-1:580247275435:layer:LambdaInsightsExtension:32", 17 | "cn-north-1": "arn:aws-cn:lambda:cn-north-1:488211338238:layer:LambdaInsightsExtension:26", 18 | "cn-northwest-1": "arn:aws-cn:lambda:cn-northwest-1:488211338238:layer:LambdaInsightsExtension:26", 19 | "eu-central-1": "arn:aws:lambda:eu-central-1:580247275435:layer:LambdaInsightsExtension:35", 20 | "eu-west-1": "arn:aws:lambda:eu-west-1:580247275435:layer:LambdaInsightsExtension:33", 21 | "eu-west-2": "arn:aws:lambda:eu-west-2:580247275435:layer:LambdaInsightsExtension:33", 22 | "eu-south-1": "arn:aws:lambda:eu-south-1:339249233099:layer:LambdaInsightsExtension:25", 23 | "eu-west-3": "arn:aws:lambda:eu-west-3:580247275435:layer:LambdaInsightsExtension:32", 24 | "eu-south-2": "arn:aws:lambda:eu-south-2:352183217350:layer:LambdaInsightsExtension:10", 25 | "eu-north-1": "arn:aws:lambda:eu-north-1:580247275435:layer:LambdaInsightsExtension:30", 26 | "eu-central-2": "arn:aws:lambda:eu-central-2:033019950311:layer:LambdaInsightsExtension:7", 27 | "me-south-1": "arn:aws:lambda:me-south-1:285320876703:layer:LambdaInsightsExtension:25", 28 | "me-central-1": "arn:aws:lambda:me-central-1:732604637566:layer:LambdaInsightsExtension:9", 29 | "sa-east-1": "arn:aws:lambda:sa-east-1:580247275435:layer:LambdaInsightsExtension:32" 30 | } -------------------------------------------------------------------------------- /layerVersionsArm64.json: -------------------------------------------------------------------------------- 1 | { 2 | "us-east-1": "arn:aws:lambda:us-east-1:580247275435:layer:LambdaInsightsExtension-Arm64:2", 3 | "us-east-2": "arn:aws:lambda:us-east-2:580247275435:layer:LambdaInsightsExtension-Arm64:2", 4 | "us-west-2": "arn:aws:lambda:us-west-2:580247275435:layer:LambdaInsightsExtension-Arm64:2", 5 | "ap-south-1": "arn:aws:lambda:ap-south-1:580247275435:layer:LambdaInsightsExtension-Arm64:2", 6 | "ap-southeast-1": "arn:aws:lambda:ap-southeast-1:580247275435:layer:LambdaInsightsExtension-Arm64:2", 7 | "ap-southeast-2": "arn:aws:lambda:ap-southeast-2:580247275435:layer:LambdaInsightsExtension-Arm64:2", 8 | "ap-northeast-1": "arn:aws:lambda:ap-northeast-1:580247275435:layer:LambdaInsightsExtension-Arm64:2", 9 | "eu-central-1": "arn:aws:lambda:eu-central-1:580247275435:layer:LambdaInsightsExtension-Arm64:2", 10 | "eu-west-1": "arn:aws:lambda:eu-west-1:580247275435:layer:LambdaInsightsExtension-Arm64:2", 11 | "eu-west-2": "arn:aws:lambda:eu-west-2:580247275435:layer:LambdaInsightsExtension-Arm64:2" 12 | } 13 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "serverless-plugin-lambda-insights", 3 | "version": "2.0.0", 4 | "description": "A Serverless Framework Plugin allowing to enable Lambda Insights for entire Serverless stack or individual functions", 5 | "main": "index.js", 6 | "scripts": { 7 | "lint": "eslint", 8 | "test": "jest" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git+https://github.com/awslabs/serverless-plugin-lambda-insights.git" 13 | }, 14 | "keywords": [ 15 | "serverless", 16 | "aws", 17 | "Serverless Plugin", 18 | "Lambda Insights", 19 | "Metric" 20 | ], 21 | "author": { 22 | "name": "Amazon Web Services", 23 | "url": "https://aws.amazon.com" 24 | }, 25 | "license": "MIT-0", 26 | "bugs": { 27 | "url": "https://github.com/awslabs/serverless-plugin-lambda-insights/issues" 28 | }, 29 | "homepage": "https://github.com/awslabs/serverless-plugin-lambda-insights#readme", 30 | "devDependencies": { 31 | "eslint": "^7.32.0", 32 | "eslint-config-google": "^0.14.0", 33 | "husky": "^6.0.0", 34 | "jest": "^27.2.4", 35 | "lint-staged": "^11.1.2" 36 | }, 37 | "lint-staged": { 38 | "*.js": "eslint" 39 | }, 40 | "husky": { 41 | "hooks": { 42 | "pre-commit": "lint-staged" 43 | } 44 | } 45 | } 46 | --------------------------------------------------------------------------------