diff --git a/packages/cloudwatch-logs-auto-subscribe/README.md b/packages/cloudwatch-logs-auto-subscribe/README.md index 6651087..52683a4 100644 --- a/packages/cloudwatch-logs-auto-subscribe/README.md +++ b/packages/cloudwatch-logs-auto-subscribe/README.md @@ -56,3 +56,7 @@ For more details, read this [post](https://theburningmonk.com/2019/05/how-to-inc `FilterPattern`: (Optional) if specified, will override the filter pattern used to create the subscription. `UnsubscribeOnDelete`: (Optional) whether to remove the subscription filters that were added by this app. Defaults to "false", allowed values are "true" or "false". + +`SleepBetweenMaxQuotaRequests`: (optional) Amount of time in milliseconds to sleep after making {AwsMaxRequestsPerSecQuota} consecutive requests. It might be useful if an AWS account has a large amount of LogGroups or if experiencing Rate Exceeded throttling frequently. Suggested to use a value equal or bellow 1000, as quotas are per second. Defaults to 0. + +`AwsMaxRequestsPerSecQuota`: (optional) AWS quota for the max number of requests per second on DescribeLogGroups. This parameter is only used when SleepBetweenMaxQuotaRequests is greater than 0. More info: https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/cloudwatch_limits_cwl.html. Defaults to 5. diff --git a/packages/cloudwatch-logs-auto-subscribe/functions/subscribe.js b/packages/cloudwatch-logs-auto-subscribe/functions/subscribe.js index cf44ed1..41b5e1e 100644 --- a/packages/cloudwatch-logs-auto-subscribe/functions/subscribe.js +++ b/packages/cloudwatch-logs-auto-subscribe/functions/subscribe.js @@ -3,17 +3,27 @@ const cloudWatchLogs = require("./lib/cloudwatch-logs"); const log = require("@dazn/lambda-powertools-logger"); const { FILTER_NAME, DESTINATION_ARN } = process.env; +const SLEEP_BETWEEN_MAX_QUOTA_REQUESTS = parseInt(process.env.SLEEP_BETWEEN_MAX_QUOTA_REQUESTS, 10); +const AWS_MAX_REQUESTS_PER_SEC_QUOTA = parseInt(process.env.AWS_MAX_REQUESTS_PER_SEC_QUOTA, 10); + +function sleep(milliseconds) { + return new Promise(r => setTimeout(r, milliseconds)); +} module.exports.existingLogGroups = async () => { const logGroupNames = await cloudWatchLogs.getLogGroups(); - for (const logGroupName of logGroupNames) { + for (let log_group_index = 0; log_group_index < logGroupNames.length; log_group_index++) { + const logGroupName = logGroupNames[log_group_index]; try { - if (await filter(logGroupName)) { + if (await filter(logGroupName)) { await subscribe(logGroupName); } } catch(error) { log.warn("cannot process existing log group, skipped...", { logGroupName }, error); } + if (SLEEP_BETWEEN_MAX_QUOTA_REQUESTS && ((log_group_index + 1) % AWS_MAX_REQUESTS_PER_SEC_QUOTA == 0)) { + await sleep(SLEEP_BETWEEN_MAX_QUOTA_REQUESTS); + } } }; @@ -154,3 +164,4 @@ const unsubscribe = async (logGroupName) => { log.error("failed to unsubscribe log group", { logGroupName }, err); } }; + diff --git a/packages/cloudwatch-logs-auto-subscribe/functions/subscribe.test.js b/packages/cloudwatch-logs-auto-subscribe/functions/subscribe.test.js index 85e5642..1e4a17d 100644 --- a/packages/cloudwatch-logs-auto-subscribe/functions/subscribe.test.js +++ b/packages/cloudwatch-logs-auto-subscribe/functions/subscribe.test.js @@ -20,6 +20,8 @@ console.log = jest.fn(); beforeEach(() => { process.env.RETRY_MIN_TIMEOUT = "100"; process.env.RETRY_MAX_TIMEOUT = "100"; + process.env.SLEEP_BETWEEN_MAX_QUOTA_REQUESTS = "0"; + process.env.AWS_MAX_REQUESTS_PER_SEC_QUOTA = "5"; process.env.FILTER_NAME = "ship-logs"; process.env.TAGS_MODE = "OR"; process.env.EXCLUDE_TAGS_MODE = "OR"; diff --git a/packages/cloudwatch-logs-auto-subscribe/template.yml b/packages/cloudwatch-logs-auto-subscribe/template.yml index a8df517..ed65813 100644 --- a/packages/cloudwatch-logs-auto-subscribe/template.yml +++ b/packages/cloudwatch-logs-auto-subscribe/template.yml @@ -31,6 +31,8 @@ Globals: FILTER_PATTERN: !Ref FilterPattern ROLE_ARN: !GetAtt CloudWatchToKinesisRole.Arn LOG_LEVEL: INFO + SLEEP_BETWEEN_MAX_QUOTA_REQUESTS: !Ref SleepBetweenMaxQuotaRequests + AWS_MAX_REQUESTS_PER_SEC_QUOTA: !Ref AwsMaxRequestsPerSecQuota Conditions: Unsubscribe: !Equals [!Ref UnsubscribeOnDelete, 'true'] @@ -271,3 +273,17 @@ Parameters: Default: 6 Description: > (optional) whether to increase the default lambda timeout". + SleepBetweenMaxQuotaRequests: + Type: Number + Default: 0 + Description: > + (optional) Amount of time to sleep after making {AwsMaxRequestsPerSecQuota} consecutive requests. + It might be useful if an AWS account has a large amount of LogGroups or if experiencing Rate Exceeded throttling + frequently". + AwsMaxRequestsPerSecQuota: + Type: Number + Default: 5 + Description: > + (optional) AWS quota for the max number of requests per second on DescribeLogGroups. + This parameter is only used when SleepBetweenMaxQuotaRequests is greater than 0. + More info: https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/cloudwatch_limits_cwl.html. Defaults to 5".