diff --git a/docs/providers/aws/events/apigateway.md b/docs/providers/aws/events/apigateway.md index c6a06d435..3aaffb62f 100644 --- a/docs/providers/aws/events/apigateway.md +++ b/docs/providers/aws/events/apigateway.md @@ -1507,3 +1507,16 @@ provider: restApi: format: '{ "requestId":"$context.requestId", "ip": "$context.identity.sourceIp" }' ``` + +The default API Gateway log level will be INFO. You can change this to error with the following: + +```yml +# serverless.yml +provider: + name: aws + logs: + restApi: + level: ERROR +``` + +Valid values are INFO, ERROR. diff --git a/docs/providers/aws/guide/serverless.yml.md b/docs/providers/aws/guide/serverless.yml.md index c9e6d573f..d108e732c 100644 --- a/docs/providers/aws/guide/serverless.yml.md +++ b/docs/providers/aws/guide/serverless.yml.md @@ -130,7 +130,9 @@ provider: apiGateway: true lambda: true # Optional, can be true (true equals 'Active'), 'Active' or 'PassThrough' logs: - restApi: true # Optional configuration which specifies if API Gateway logs are used + restApi: # Optional configuration which specifies if API Gateway logs are used. This can either be set to true to use defaults, or configured via subproperties. + format: 'requestId: $context.requestId' # Optional configuration which specifies the log format to use. + level: INFO # Optional configuration which specifies the log level to use. May be either INFO or ERROR. websocket: true # Optional configuration which specifies if Websockets logs are used package: # Optional deployment packaging configuration diff --git a/lib/plugins/aws/package/compile/events/apiGateway/lib/hack/updateStage.js b/lib/plugins/aws/package/compile/events/apiGateway/lib/hack/updateStage.js index 8f5d2834a..710b43b46 100644 --- a/lib/plugins/aws/package/compile/events/apiGateway/lib/hack/updateStage.js +++ b/lib/plugins/aws/package/compile/events/apiGateway/lib/hack/updateStage.js @@ -2,6 +2,7 @@ const _ = require('lodash'); const BbPromise = require('bluebird'); +const ServerlessError = require('../../../../../../../../classes/Error').ServerlessError; const isRestApiId = RegExp.prototype.test.bind(/^[a-z0-9]{3,}$/); const defaultApiGatewayLogFormat = [ @@ -16,6 +17,8 @@ const defaultApiGatewayLogFormat = [ 'protocol: $context.protocol', 'responseLength: $context.responseLength', ].join(', '); +const defaultApiGatewayLogLevel = 'INFO'; +const apiGatewayValidLogLevels = new Set(['INFO', 'ERROR']); // NOTE --> Keep this file in sync with ../stage.js @@ -23,6 +26,8 @@ const defaultApiGatewayLogFormat = [ // Stage resource (see https://github.com/serverless/serverless/pull/5692#issuecomment-467849311 for more information). module.exports = { + defaultApiGatewayLogLevel, + apiGatewayValidLogLevels, updateStage() { // this array is used to gather all the patch operations we need to // perform on the stage @@ -191,6 +196,18 @@ function handleLogs() { logFormat = logs.format; } + let level = defaultApiGatewayLogLevel; + if (logs.level) { + level = logs.level; + if (!apiGatewayValidLogLevels.has(level)) { + throw new ServerlessError( + `provider.logs.restApi.level is set to an invalid value. Support values are ${Array.from( + apiGatewayValidLogLevels + ).join(', ')}, got ${level}.` + ); + } + } + const destinationArn = { op: 'replace', path: '/accessLogSettings/destinationArn', @@ -202,7 +219,7 @@ function handleLogs() { value: logFormat, }; dataTrace = { op: 'replace', path: '/*/*/logging/dataTrace', value: 'true' }; - logLevel = { op: 'replace', path: '/*/*/logging/loglevel', value: 'INFO' }; + logLevel = { op: 'replace', path: '/*/*/logging/loglevel', value: level }; operations = [destinationArn, format, dataTrace, logLevel]; } diff --git a/lib/plugins/aws/package/compile/events/apiGateway/lib/hack/updateStage.test.js b/lib/plugins/aws/package/compile/events/apiGateway/lib/hack/updateStage.test.js index 1c6e79297..f5c23fb83 100644 --- a/lib/plugins/aws/package/compile/events/apiGateway/lib/hack/updateStage.test.js +++ b/lib/plugins/aws/package/compile/events/apiGateway/lib/hack/updateStage.test.js @@ -8,9 +8,14 @@ const sinon = require('sinon'); const _ = require('lodash'); const Serverless = require('../../../../../../../../Serverless'); const AwsProvider = require('../../../../../../provider/awsProvider'); -const updateStage = require('./updateStage').updateStage; +const { + updateStage, + apiGatewayValidLogLevels, + defaultApiGatewayLogLevel, +} = require('./updateStage'); chai.use(require('sinon-chai')); +chai.use(require('chai-as-promised')); const { expect } = chai; @@ -389,4 +394,45 @@ describe('#updateStage()', () => { }); }); }); + + function checkLogLevel(setLevel, expectedLevel) { + if (setLevel) { + context.state.service.provider.logs = { + restApi: { + level: setLevel, + }, + }; + } else { + context.state.service.provider.logs = { + restApi: true, + }; + } + + return updateStage.call(context).then(() => { + const patchOperation = { op: 'replace', path: '/*/*/logging/loglevel', value: expectedLevel }; + + const patchOperations = providerRequestStub.args[2][2].patchOperations; + expect(patchOperations).to.include.deep.members([patchOperation]); + }); + } + + it('should use the default log level if no log level is given', () => { + return checkLogLevel(null, defaultApiGatewayLogLevel); + }); + + apiGatewayValidLogLevels.forEach(logLevel => { + it(`should update the stage with a custom APIGW log level if given ${logLevel}`, () => { + return checkLogLevel(logLevel, logLevel); + }); + }); + + it('should reject a custom APIGW log level if value is invalid', () => { + context.state.service.provider.logs = { + restApi: { + level: 'INVALID', + }, + }; + + return expect(updateStage.call(context)).to.be.rejectedWith('invalid value'); + }); });