├── .gitignore ├── LICENSE ├── README.md ├── add-log-retention.js ├── package.json └── test └── add-log-retention.js /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (http://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # Typescript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env 59 | 60 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 ArtificerEntertainment 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | > [!IMPORTANT] 2 | > The Serverless Framework now has built-in support for the log retention setting. [For more information please refer to the official documentation](https://www.serverless.com/framework/docs/providers/aws/guide/serverless.yml#general-aws-lambda-settings). This plugin should no longer be needed. 3 | 4 | # serverless-plugin-log-retention 5 | Control the retention of your serverless function's cloudwatch logs. 6 | 7 | ## Usage example 8 | `serverless.yml` 9 | 10 | ```yml 11 | service: sample 12 | 13 | plugins: 14 | - serverless-plugin-log-retention 15 | 16 | provider: 17 | name: aws 18 | 19 | custom: 20 | logRetentionInDays: 30 # used to set a global value for all functions 21 | 22 | functions: 23 | function1: 24 | function2: 25 | logRetentionInDays: 10 # set the retention for specific log group 26 | ``` 27 | -------------------------------------------------------------------------------- /add-log-retention.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const nco = require('nco'); 4 | const semver = require('semver'); 5 | 6 | //values from http://docs.aws.amazon.com/AmazonCloudWatchLogs/latest/APIReference/API_PutRetentionPolicy.html 7 | const validRetentionInDays = [1, 3, 5, 7, 14, 30, 60, 90, 120, 150, 180, 365, 400, 545, 731, 1827, 3653]; 8 | 9 | class AwsAddLogRetention { 10 | constructor(serverless, options) { 11 | if(!semver.satisfies(serverless.version, '>= 1.20.2')) { 12 | throw new Error('serverless-plugin-log-retention requires serverless 1.20.2 or higher'); 13 | } 14 | 15 | this.serverless = serverless; 16 | this.options = options; 17 | this.provider = this.serverless.getProvider('aws'); 18 | this.hooks = { 19 | 'package:createDeploymentArtifacts': this.beforeDeploy.bind(this), 20 | }; 21 | } 22 | 23 | sanitizeRetentionValue(inputValue) { 24 | const value = Number(inputValue); 25 | if(Number.isInteger(value) && validRetentionInDays.includes(value)) { 26 | return value; 27 | } else { 28 | throw new Error(`RetentionInDays value must be one of ${validRetentionInDays}`); 29 | } 30 | } 31 | 32 | addLogRetentionForFunctions(globalLogRetentionInDays) { 33 | const service = this.serverless.service; 34 | if(typeof service.functions !== 'object') { 35 | return; 36 | } 37 | 38 | const resources = nco(service.resources, {}); 39 | resources.Resources = nco(resources.Resources, {}); 40 | 41 | Object.keys(service.functions).forEach(functionName => { 42 | const localLogRentationInDays = nco(service.functions[functionName].logRetentionInDays, null); 43 | if(localLogRentationInDays === null && globalLogRetentionInDays === null) { 44 | return; 45 | } 46 | const functionLogRetentionInDays = localLogRentationInDays === null ? globalLogRetentionInDays : this.sanitizeRetentionValue(localLogRentationInDays); 47 | const logGroupLogicalId = this.provider.naming.getLogGroupLogicalId(functionName); 48 | 49 | const resource = { 50 | Type: 'AWS::Logs::LogGroup', 51 | Properties: { 52 | RetentionInDays: functionLogRetentionInDays 53 | } 54 | }; 55 | resources.Resources[logGroupLogicalId] = resource; 56 | }); 57 | } 58 | 59 | beforeDeploy() { 60 | const service = this.serverless.service; 61 | const globalLogRetentionInDays = service.custom && service.custom.logRetentionInDays 62 | ? this.sanitizeRetentionValue(service.custom.logRetentionInDays) 63 | : null; 64 | this.addLogRetentionForFunctions(globalLogRetentionInDays); 65 | } 66 | } 67 | 68 | module.exports = AwsAddLogRetention; 69 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "serverless-plugin-log-retention", 3 | "version": "2.1.0", 4 | "description": "Control the retention of your function's cloudwatch logs.", 5 | "main": "add-log-retention.js", 6 | "scripts": { 7 | "test": "nyc --reporter=lcov --reporter=text mocha" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/ArtificerEntertainment/serverless-plugin-log-retention.git" 12 | }, 13 | "bugs": { 14 | "url": "https://github.com/ArtificerEntertainment/serverless-plugin-log-retention/issues" 15 | }, 16 | "homepage": "https://github.com/ArtificerEntertainment/serverless-plugin-log-retention", 17 | "keywords": [ 18 | "serverless", 19 | "plugin", 20 | "log", 21 | "retention" 22 | ], 23 | "author": "Artificer Entertainment", 24 | "license": "MIT", 25 | "dependencies": { 26 | "nco": "1.0.1", 27 | "semver": "7.5.3" 28 | }, 29 | "devDependencies": { 30 | "chai": "4.1.2", 31 | "mocha": "^10.2.0", 32 | "nyc": "^15.1.0", 33 | "sinon": "4.1.2" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /test/add-log-retention.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const expect = require('chai').expect; 4 | const sinon = require('sinon'); 5 | 6 | const AwsAddLogRetention = require('../add-log-retention'); 7 | 8 | function createTestInstance(options) { 9 | options = options || {}; 10 | return new AwsAddLogRetention({ 11 | version: options.version || '1.20.2', 12 | service: { 13 | provider: options.provider || {}, 14 | functions: options.functions, 15 | resources: options.resources ? { Resources: options.resources } : undefined 16 | }, 17 | getProvider: () => { 18 | return { 19 | naming: { 20 | getLogGroupLogicalId(functionName) { 21 | return `${functionName.charAt(0).toUpperCase()}${functionName.slice(1)}LogGroup`; //TODO: dash/underscore replacement? 22 | } 23 | } 24 | } 25 | } 26 | }, {}); 27 | } 28 | 29 | describe('serverless-plugin-log-retention', function() { 30 | describe('#constructor', function() { 31 | it('should throw on older version', function() { 32 | expect(() => createTestInstance({version: '1.20.1'})) 33 | .to.throw('serverless-plugin-log-retention requires serverless 1.20.2 or higher'); 34 | }); 35 | 36 | it('should create hooks', function() { 37 | const instance = createTestInstance(); 38 | expect(instance) 39 | .to.have.property('hooks') 40 | .that.has.all.keys('package:createDeploymentArtifacts'); 41 | 42 | const stub = sinon.stub(instance, 'addLogRetentionForFunctions'); 43 | instance.hooks['package:createDeploymentArtifacts'](); 44 | 45 | sinon.assert.calledOnce(stub); 46 | }); 47 | }) 48 | 49 | }); 50 | --------------------------------------------------------------------------------