CloudWatch Logsのログ自体は、保持期間を指定できるのでデータは消えてくれるけど、データの入れ物であるlogstreamだけは空のものが延々と残り続けるので削除する。
コード
こういう時はさくっとできるserverless frameworkで。
service: lambda-delete-empty-logstream frameworkVersion: '3' provider: name: aws runtime: nodejs14.x stage: dev region: ap-northeast-1 iam: role: statements: - Effect: "Allow" Action: - "logs:DescribeLogGroups" - "logs:DescribeLogStreams" - "logs:DeleteLogStream" Resource: "*" functions: main: handler: handler.main description: Delete old and empty logstream timeout: 300 events: - schedule: rate(1 day) resources: Description: Delete old and empty logstream
'use strict'; const AWS = require('aws-sdk'); const Logs = new AWS.CloudWatchLogs(); // rate limit is 5 calls/sec. so sleep 200sec // https://docs.aws.amazon.com/ja_jp/AmazonCloudWatch/latest/logs/cloudwatch_limits_cwl.html const sleep = () => new Promise((resolve) => setTimeout(resolve, 200)); module.exports.main = async (event) => { const now = Date.now(); const threshold = 7 * 60 * 60 * 24 * 1000; let cnt = 0; let groups; groups = await Logs.describeLogGroups({}).promise(); while (1) { for (const log of groups.logGroups) { let streams; streams = await Logs.describeLogStreams({ logGroupName: log.logGroupName }).promise(); while (1) { for (const stream of streams.logStreams) { if (stream.storedBytes === 0 && (now - stream.lastEventTimestamp) > threshold) { console.log("DELETE", ++cnt, log.logGroupName, stream.logStreamName); await Logs.deleteLogStream({ logGroupName: log.logGroupName, logStreamName: stream.logStreamName }).promise(); await sleep(); } } if (streams.nextToken) { console.log("FETCH stream"); streams = await Logs.describeLogStreams({ logGroupName: log.logGroupName, nextToken: streams.nextToken }).promise(); await sleep(); } else { break; } } } if (groups.nextToken) { console.log("FETCH group"); groups = await Logs.describeLogGroups({ nextToken }).promise(); } else { break; } } return "OK"; };
実行結果
ドキュメントを確認したところ、logstream一覧や削除のAPIは秒間5回が限度らしい
なので1callごとに200msのsleepを入れるようにした。
lambdaの実行時間上限値の300秒に設定して実行してみたら、logstreamを1000個くらい消したあとタイムアウトして終了する。だーーっと流して一斉に消してもいいけれど、急いでるものでもないし一日に一回だけ流す設定にしてみた。いつか消せるものは全部消えてくれるでしょう。たぶんまた増えるだろうし、一日一回のバッチ処理で徐々に消せばいいかなという感じ。
作った後にEventBridgeでログを消えた瞬間にlogstreamを消すやつ作れそうじゃね?とも思ったが、暇があったらまた調べようと思う。