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

Disable CloudWatch Log Group per function #7720

Merged
merged 5 commits into from May 13, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
9 changes: 9 additions & 0 deletions docs/providers/aws/guide/functions.md
Expand Up @@ -361,6 +361,15 @@ To publish Lambda Layers, check out the [Layers](./layers.md) documentation.

By default, the framework will create LogGroups for your Lambdas. This makes it easy to clean up your log groups in the case you remove your service, and make the lambda IAM permissions much more specific and secure.

You can opt out of the default behavior by setting `disableLogs: true`

```yml
functions:
hello:
handler: handler.hello
disableLogs: true
```

## Versioning Deployed Functions

By default, the framework creates function versions for every deploy. This behavior is optional, and can be turned off in cases where you don't invoke past versions by their qualifier. If you would like to do this, you can invoke your functions as `arn:aws:lambda:....:function/myFunc:3` to invoke version 3 for example.
Expand Down
1 change: 1 addition & 0 deletions docs/providers/aws/guide/serverless.yml.md
Expand Up @@ -39,6 +39,7 @@ provider:
reservedConcurrency: 5 # optional, Overwrite the default reserved concurrency limit. By default, AWS uses account concurrency limit
timeout: 10 # The default is 6 seconds. Note: API Gateway current maximum is 30 seconds
logRetentionInDays: 14 # Set the default RetentionInDays for a CloudWatch LogGroup
disableLogs: false # Disables creation of CloudWatch Log Group
deploymentBucket:
name: com.serverless.${self:provider.region}.deploys # Deployment bucket name. Default is generated by the framework
maxPreviousDeploymentArtifacts: 10 # On every deployment the framework prunes the bucket to remove artifacts older than this limit. The default is 5
Expand Down
41 changes: 22 additions & 19 deletions lib/plugins/aws/package/lib/mergeIamTemplates.js
Expand Up @@ -18,28 +18,31 @@ module.exports = {
}

// create log group resources
this.serverless.service.getAllFunctions().forEach(functionName => {
const functionObject = this.serverless.service.getFunction(functionName);
const logGroupLogicalId = this.provider.naming.getLogGroupLogicalId(functionName);
const newLogGroup = {
[logGroupLogicalId]: {
Type: 'AWS::Logs::LogGroup',
Properties: {
LogGroupName: this.provider.naming.getLogGroupName(functionObject.name),
this.serverless.service
.getAllFunctions()
.filter(functionName => !this.serverless.service.getFunction(functionName).disableLogs)
.forEach(functionName => {
const functionObject = this.serverless.service.getFunction(functionName);
const logGroupLogicalId = this.provider.naming.getLogGroupLogicalId(functionName);
const newLogGroup = {
[logGroupLogicalId]: {
Type: 'AWS::Logs::LogGroup',
Properties: {
LogGroupName: this.provider.naming.getLogGroupName(functionObject.name),
},
},
},
};
};

const logRetentionInDays = this.provider.getLogRetentionInDays();
if (logRetentionInDays) {
newLogGroup[logGroupLogicalId].Properties.RetentionInDays = logRetentionInDays;
}
const logRetentionInDays = this.provider.getLogRetentionInDays();
if (logRetentionInDays) {
newLogGroup[logGroupLogicalId].Properties.RetentionInDays = logRetentionInDays;
}

_.merge(
this.serverless.service.provider.compiledCloudFormationTemplate.Resources,
newLogGroup
);
});
_.merge(
this.serverless.service.provider.compiledCloudFormationTemplate.Resources,
newLogGroup
);
});

// resolve early if provider level role is provided
if ('role' in this.serverless.service.provider) {
Expand Down
72 changes: 56 additions & 16 deletions lib/plugins/aws/package/lib/mergeIamTemplates.test.js
@@ -1,6 +1,8 @@
'use strict';

const expect = require('chai').expect;
const runServerless = require('../../../../../tests/utils/run-serverless');
const fixtures = require('../../../../../tests/fixtures');

const Serverless = require('../../../../Serverless');
const AwsProvider = require('../../provider/awsProvider');
Expand Down Expand Up @@ -606,22 +608,6 @@ describe('#mergeIamTemplates()', () => {
);
});

it('should add a CloudWatch LogGroup resource', () => {
const normalizedName = awsPackage.provider.naming.getLogGroupLogicalId(functionName);
return awsPackage.mergeIamTemplates().then(() => {
expect(
awsPackage.serverless.service.provider.compiledCloudFormationTemplate.Resources[
normalizedName
]
).to.deep.equal({
Type: 'AWS::Logs::LogGroup',
Properties: {
LogGroupName: awsPackage.provider.naming.getLogGroupName(resolvedFunctionName),
},
});
});
});

it('should add RetentionInDays to a CloudWatch LogGroup resource if logRetentionInDays is given', () =>
Promise.all(
[5, '5'].map(logRetentionInDays => {
Expand Down Expand Up @@ -885,3 +871,57 @@ describe('#mergeIamTemplates()', () => {
});
});
});

describe('#mergeIamTemplates() disableLogs flag', () => {
const functionName = 'foo';
const fixtureName = 'function';
function extendAndPackage(extension) {
return fixtures
.extend(fixtureName, extension)
.then(fixturePath => runServerless({ cwd: fixturePath, cliArgs: ['package'] }))
.then(serverless => {
const compiledCloudFormationTemplate =
serverless.service.provider.compiledCloudFormationTemplate;
const resolvedName = serverless.service.getFunction(functionName).name;
const normalizedName = serverless.providers.aws.naming.getLogGroupLogicalId(functionName);
const logGroupName = serverless.providers.aws.naming.getLogGroupName(resolvedName);
return { compiledCloudFormationTemplate, logGroupName, normalizedName };
});
}

after(fixtures.cleanup);

it('should add a CloudWatch LogGroup resource if disableLogs flag not provided', () => {
return extendAndPackage({}).then(
({ compiledCloudFormationTemplate, logGroupName, normalizedName }) => {
expect(compiledCloudFormationTemplate.Resources[normalizedName]).to.deep.equal({
Type: 'AWS::Logs::LogGroup',
Properties: {
LogGroupName: logGroupName,
},
});
}
);
});

it('should add a CloudWatch LogGroup resource if disableLogs is false', () => {
return extendAndPackage({ functions: { [functionName]: { disableLogs: false } } }).then(
({ compiledCloudFormationTemplate, logGroupName, normalizedName }) => {
expect(compiledCloudFormationTemplate.Resources[normalizedName]).to.deep.equal({
Type: 'AWS::Logs::LogGroup',
Properties: {
LogGroupName: logGroupName,
},
});
}
);
});

it('should not add a CloudWatch LogGroup resource if disableLogs is true', () => {
return extendAndPackage({ functions: { [functionName]: { disableLogs: true } } }).then(
({ compiledCloudFormationTemplate, normalizedName }) => {
expect(compiledCloudFormationTemplate.Resources[normalizedName]).to.be.undefined;
}
);
});
});
1 change: 1 addition & 0 deletions tests/.gitignore
@@ -0,0 +1 @@
.serverless/