Skip to content

Commit

Permalink
Merge pull request #6057 from serverless/api-gateway-logs
Browse files Browse the repository at this point in the history
Add support for API Gateway REST API Logs
  • Loading branch information
pmuens committed May 6, 2019
2 parents fc76d03 + 6f51cab commit 2c97649
Show file tree
Hide file tree
Showing 6 changed files with 309 additions and 20 deletions.
22 changes: 19 additions & 3 deletions docs/providers/aws/events/apigateway.md
Expand Up @@ -47,7 +47,10 @@ layout: Doc
- [Share Authorizer](#share-authorizer)
- [Resource Policy](#resource-policy)
- [Compression](#compression)
- [AWS X-Ray Tracing](#aws-x-ray-tracing)
- [Stage specific setups](#stage-specific-setups)
- [AWS X-Ray Tracing](#aws-x-ray-tracing)
- [Tags / Stack Tags](#tags--stack-tags)
- [Logs](#logs)

_Are you looking for tutorials on using API Gateway? Check out the following resources:_

Expand Down Expand Up @@ -1421,8 +1424,7 @@ Disabling settings might result in unexpected behavior. We recommend to remove a

### AWS X-Ray Tracing

API Gateway supports a form of out of the box distributed tracing via [AWS X-Ray](https://aws.amazon.com/xray/) though enabling [active tracing](https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-xray.html). To enable this feature for your serverless
application's API Gateway add the following to your `serverless.yml`
API Gateway supports a form of out of the box distributed tracing via [AWS X-Ray](https://aws.amazon.com/xray/) though enabling [active tracing](https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-xray.html). To enable this feature for your serverless application's API Gateway add the following to your `serverless.yml`

```yml
# serverless.yml
Expand All @@ -1447,3 +1449,17 @@ provider:
tags:
tagKey: tagValue
```

### Logs

Use the following configuration to enable API Gateway logs:

```yml
# serverless.yml
provider:
name: aws
apiGateway:
logs: true
```

The log streams will be generated in a dedicated log group which follows the naming schema `/aws/api-gateway/{service}-{stage}`.
1 change: 1 addition & 0 deletions docs/providers/aws/guide/serverless.yml.md
Expand Up @@ -62,6 +62,7 @@ provider:
apiKeySourceType: HEADER # Source of API key for usage plan. HEADER or AUTHORIZER.
minimumCompressionSize: 1024 # Compress response when larger than specified size in bytes (must be between 0 and 10485760)
description: Some Description # optional description for the API Gateway stage deployment
logs: true # Optional configuration which specifies if API Gateway logs are used
usagePlan: # Optional usage plan configuration
quota:
limit: 5000
Expand Down
9 changes: 9 additions & 0 deletions lib/plugins/aws/lib/naming.js
Expand Up @@ -273,6 +273,15 @@ module.exports = {
getStageLogicalId() {
return 'ApiGatewayStage';
},
getApiGatewayLogGroupLogicalId() {
return 'ApiGatewayLogGroup';
},
getApiGatewayLogsRoleLogicalId() {
return 'IamRoleApiGatewayLogs';
},
getApiGatewayAccountLogicalId() {
return 'ApiGatewayAccount';
},

// S3
getDeploymentBucketLogicalId() {
Expand Down
18 changes: 18 additions & 0 deletions lib/plugins/aws/lib/naming.test.js
Expand Up @@ -462,6 +462,24 @@ describe('#naming()', () => {
});
});

describe('#getApiGatewayLogGroupLogicalId()', () => {
it('should return the API Gateway log group logical id', () => {
expect(sdk.naming.getApiGatewayLogGroupLogicalId()).to.equal('ApiGatewayLogGroup');
});
});

describe('#getApiGatewayLogsRoleLogicalId()', () => {
it('should return the API Gateway logs IAM role logical id', () => {
expect(sdk.naming.getApiGatewayLogsRoleLogicalId()).to.equal('IamRoleApiGatewayLogs');
});
});

describe('#getApiGatewayAccountLogicalId()', () => {
it('should return the API Gateway account logical id', () => {
expect(sdk.naming.getApiGatewayAccountLogicalId()).to.equal('ApiGatewayAccount');
});
});

describe('#getDeploymentBucketLogicalId()', () => {
it('should return "ServerlessDeploymentBucket"', () => {
expect(sdk.naming.getDeploymentBucketLogicalId()).to.equal('ServerlessDeploymentBucket');
Expand Down
141 changes: 124 additions & 17 deletions lib/plugins/aws/package/compile/events/apiGateway/lib/stage.js
@@ -1,11 +1,19 @@
/* eslint-disable no-use-before-define */

'use strict';

const _ = require('lodash');
const BbPromise = require('bluebird');

module.exports = {
compileStage() {
const service = this.serverless.service.service;
const stage = this.options.stage;
const provider = this.serverless.service.provider;
const cfTemplate = this.serverless.service.provider.compiledCloudFormationTemplate;

// logs
const logs = provider.apiGateway && provider.apiGateway.logs;

// TracingEnabled
const tracing = provider.tracing;
Expand All @@ -25,37 +33,136 @@ module.exports = {

// NOTE: the DeploymentId is random, therefore we rely on prior usage here
const deploymentId = this.apiGatewayDeploymentLogicalId;
const logGrouLogicalId = this.provider.naming
.getApiGatewayLogGroupLogicalId();
const logsRoleLogicalId = this.provider.naming
.getApiGatewayLogsRoleLogicalId();
const accountLogicalid = this.provider.naming
.getApiGatewayAccountLogicalId();

this.apiGatewayStageLogicalId = this.provider.naming
.getStageLogicalId();

// NOTE: right now we're only using a dedicated Stage resource
// - if AWS X-Ray tracing is enabled
// - if Tags are provided
// - if logs are enabled
// We'll change this in the future so that users can
// opt-in for other features as well
if (TracingEnabled || Tags.length > 0) {
_.merge(this.serverless.service.provider.compiledCloudFormationTemplate.Resources, {
[this.apiGatewayStageLogicalId]: {
Type: 'AWS::ApiGateway::Stage',
Properties: {
DeploymentId: {
Ref: deploymentId,
},
RestApiId: this.provider.getApiGatewayRestApiId(),
StageName: this.provider.getStage(),
TracingEnabled,
Tags,
if (logs || TracingEnabled || Tags.length > 0) {
const stageResource = {
Type: 'AWS::ApiGateway::Stage',
Properties: {
DeploymentId: {
Ref: deploymentId,
},
RestApiId: this.provider.getApiGatewayRestApiId(),
StageName: this.provider.getStage(),
TracingEnabled,
Tags,
},
});
};

// create log-specific resources
if (logs) {
_.merge(stageResource.Properties, {
AccessLogSetting: {
DestinationArn: {
'Fn::GetAtt': [
'ApiGatewayLogGroup',
'Arn',
],
},
// eslint-disable-next-line
Format: 'requestId: $context.requestId, ip: $context.identity.sourceIp, caller: $context.identity.caller, user: $context.identity.user, requestTime: $context.requestTime, httpMethod: $context.httpMethod, resourcePath: $context.resourcePath, status: $context.status, protocol: $context.protocol, responseLength: $context.responseLength',
},
MethodSettings: [
{
DataTraceEnabled: true,
HttpMethod: '*',
ResourcePath: '/*',
LoggingLevel: 'INFO',
},
],
});

_.merge(cfTemplate.Resources, {
[logGrouLogicalId]: getLogGroupResource(service, stage),
[logsRoleLogicalId]: getIamRoleResource(service, stage),
[accountLogicalid]: getAccountResource(logsRoleLogicalId),
});
}

_.merge(cfTemplate.Resources, { [this.apiGatewayStageLogicalId]: stageResource });

// we need to remove the stage name from the Deployment resource
delete this.serverless.service.provider.compiledCloudFormationTemplate
.Resources[deploymentId]
.Properties
.StageName;
delete cfTemplate.Resources[deploymentId].Properties.StageName;
}

return BbPromise.resolve();
},
};

function getLogGroupResource(service, stage) {
return ({
Type: 'AWS::Logs::LogGroup',
Properties: {
LogGroupName: `/aws/api-gateway/${service}-${stage}`,
},
});
}

function getIamRoleResource(service, stage) {
return ({
Type: 'AWS::IAM::Role',
Properties: {
AssumeRolePolicyDocument: {
Version: '2012-10-17',
Statement: [
{
Effect: 'Allow',
Principal: {
Service: [
'apigateway.amazonaws.com',
],
},
Action: [
'sts:AssumeRole',
],
},
],
},
ManagedPolicyArns: [
'arn:aws:iam::aws:policy/service-role/AmazonAPIGatewayPushToCloudWatchLogs',
],
Path: '/',
RoleName: {
'Fn::Join': [
'-',
[
service,
stage,
{
Ref: 'AWS::Region',
},
'apiGatewayLogsRole',
],
],
},
},
});
}

function getAccountResource(logsRoleLogicalId) {
return ({
Type: 'AWS::ApiGateway::Account',
Properties: {
CloudWatchRoleArn: {
'Fn::GetAtt': [
logsRoleLogicalId,
'Arn',
],
},
},
});
}

0 comments on commit 2c97649

Please sign in to comment.