Skip to content

Commit 773ca27

Browse files
committed
lambdas now take parameters
1 parent 818fd72 commit 773ca27

File tree

4 files changed

+106
-43
lines changed

4 files changed

+106
-43
lines changed

README.md

+35-12
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,39 @@
11
# AWS Cloudwatch Log minder
2-
the AWS Cloudwatch Log minder, has two tasks:
3-
- remove empty log streams older than the retention period of the log group
4-
- set a default retention period on log groups without a period set.
2+
AWS CloudWatch logs is an useful logging system, but it has two quircks. It does not allow you too set a default
3+
retention period for newly created log groups, and it does not delete empty log streams that are older than
4+
the retention period. This utility provides two functions:
5+
6+
1. set a default retention period on log groups without a period set.
7+
1. remove empty log streams older than the retention period of the log group
8+
9+
You can use it as a command line utility, or better, install it as an AWS Lambda function and have your
10+
log kept in order every day, NoOps style!
11+
12+
## install the log minder
13+
to install the log minder, type:
14+
15+
```sh
16+
pip install aws-cloudwatch-log-minder
17+
```
18+
19+
## set default retention period
20+
to set the default retention period on log groups without one, type:
21+
```sh
22+
cwlogs-minder --dry-run set-log-retention --days 30
23+
```
24+
This will show you which log groups, will have its retention period set. Remove the `--dry-run` and
25+
it will actually be done.
26+
27+
## delete empty log streams
28+
To delete empty log streams older than the retention period, type:
29+
```sh
30+
cwlogs-minder --dry-run delete-empty-log-streams
31+
```
32+
This will show you which empty log streams will be deleted. Remove the `--dry-run` and
33+
they actually will.
534

635
## deploy the log minder
7-
To deploy the log minder, type:
36+
To deploy the log minder as an AWS Lambda, type:
837

938
```sh
1039
git clone https://github.com/binxio/aws-cloudwatch-log-minder.git
@@ -16,11 +45,5 @@ aws cloudformation create-stack \
1645

1746
aws cloudformation wait stack-create-complete --stack-name aws-cloudwatch-log-minder
1847
```
19-
20-
It is scheduled to run every day around midnight.
21-
22-
You can also use the command line utility:
23-
24-
```sh
25-
pip install aws-cloudwatch-log-minder
26-
```
48+
This will install the log minder in your AWS account and run around midnight. First setting
49+
retention periods and delete the empty log streams secondly.

cloudformation/aws-cloudwatch-log-minder.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ Resources:
2525
Action:
2626
- logs:DescribeLogGroups
2727
- logs:DescribeLogStreams
28-
- log:PutRetentionPolicy
28+
- logs:PutRetentionPolicy
2929
- logs:DeleteLogStreams
3030
Resource: "*"
3131

src/aws_cloudwatch_log_minder/delete_empty_log_streams.py

+39-18
Original file line numberDiff line numberDiff line change
@@ -4,45 +4,66 @@
44
from .logging import log
55
from datetime import datetime
66

7-
cw_logs = boto3.client('logs')
7+
cw_logs = boto3.client("logs")
88

99

1010
def _delete_empty_log_streams(group: dict, oldest_in_ms: int, dry_run: bool):
1111
log_group_name = group["logGroupName"]
1212
kwargs = {
1313
"logGroupName": log_group_name,
14-
"orderBy": 'LastEventTime',
15-
"descending": False
14+
"orderBy": "LastEventTime",
15+
"descending": False,
1616
}
17-
for response in cw_logs.get_paginator('describe_log_streams').paginate(**kwargs):
18-
for stream in response['logStreams']:
19-
log_stream_name = stream['logStreamName']
17+
for response in cw_logs.get_paginator("describe_log_streams").paginate(**kwargs):
18+
for stream in response["logStreams"]:
19+
log_stream_name = stream["logStreamName"]
2020
if stream["creationTime"] > oldest_in_ms:
21-
log.debug('keeping log stream %s from group %s as it is within retention period', log_stream_name, log_group_name)
21+
log.debug(
22+
"keeping log stream %s from group %s as it is within retention period",
23+
log_stream_name,
24+
log_group_name,
25+
)
2226
continue
2327

2428
if not stream["storedBytes"] == 0 and stream["creationTime"] < oldest_in_ms:
2529
try:
26-
log.info('deleting log stream %s from group %s', log_stream_name, log_group_name)
30+
log.info(
31+
"deleting log stream %s from group %s",
32+
log_stream_name,
33+
log_group_name,
34+
)
2735
if dry_run:
2836
continue
2937
cw_logs.delete_log_stream(
30-
logGroupName=log_group_name,
31-
logStreamName=log_stream_name
38+
logGroupName=log_group_name, logStreamName=log_stream_name
3239
)
3340
except ClientError as e:
34-
log.error('failed to delete log stream %s from group %s, %s', log_stream_name, log_group_name, e)
41+
log.error(
42+
"failed to delete log stream %s from group %s, %s",
43+
log_stream_name,
44+
log_group_name,
45+
e,
46+
)
3547
else:
36-
log.debug('keeping log stream %s from group %s', log_stream_name, log_group_name)
48+
log.debug(
49+
"keeping log stream %s from group %s",
50+
log_stream_name,
51+
log_group_name,
52+
)
53+
3754

3855
def delete_empty_log_streams(dry_run: bool = False):
39-
log.info('cleaning empty log streams older than the retention period of the group')
56+
log.info("cleaning empty log streams older than the retention period of the group")
4057
now = (datetime.utcnow() - datetime(1970, 1, 1)).total_seconds()
41-
for response in cw_logs.get_paginator('describe_log_groups').paginate():
42-
for group in response['logGroups']:
43-
oldest = now - (group.get('retentionInDays', 365) * 24 * 3600)
58+
for response in cw_logs.get_paginator("describe_log_groups").paginate():
59+
for group in response["logGroups"]:
60+
oldest = now - (group.get("retentionInDays", 365) * 24 * 3600)
4461
_delete_empty_log_streams(group, oldest * 1000, dry_run)
4562

4663

47-
def handle(request, context):
48-
delete_empty_log_streams()
64+
def handle(request: dict = {}, context: dict = {}):
65+
dry_run = request.get("dry_run", False)
66+
if "dry_run" in request and not isinstance(dry_run, bool):
67+
raise ValueError(f"'dry_run' is not a boolean value, {request}")
68+
69+
delete_empty_log_streams(dry_run)

src/aws_cloudwatch_log_minder/set_log_retention.py

+31-12
Original file line numberDiff line numberDiff line change
@@ -3,31 +3,50 @@
33
from botocore.exceptions import ClientError
44
from .logging import log
55

6-
cw_logs = boto3.client('logs')
6+
cw_logs = boto3.client("logs")
77

8-
def set_log_retention(default_retention_period:int = 30, dry_run: bool = False):
9-
for response in cw_logs.get_paginator('describe_log_groups').paginate():
10-
for group in response['logGroups']:
8+
9+
def set_log_retention(retention_in_days: int = 30, dry_run: bool = False):
10+
for response in cw_logs.get_paginator("describe_log_groups").paginate():
11+
for group in response["logGroups"]:
1112
log_group_name = group["logGroupName"]
12-
current_retention = group.get('retentionInDays')
13+
current_retention = group.get("retentionInDays")
1314
if not current_retention:
1415
try:
15-
log.info('setting default retention period of log stream %s to %s', log_group_name, default_retention_period)
16+
log.info(
17+
"setting default retention period of log stream %s to %s",
18+
log_group_name,
19+
retention_in_days,
20+
)
1621
if dry_run:
1722
continue
1823
cw_logs.put_retention_policy(
1924
logGroupName=log_group_name,
20-
retentionInDays=default_retention_period
25+
retentionInDays=retention_in_days,
2126
)
2227
except ClientError as e:
23-
log.error('failed to set retention period of log stream %s to %s, %s', log_group_name, default_retention_period, e)
28+
log.error(
29+
"failed to set retention period of log stream %s to %s, %s",
30+
log_group_name,
31+
retention_in_days,
32+
e,
33+
)
2434
else:
25-
log.debug('retention period on %s already set to %s', log_group_name, current_retention)
35+
log.debug(
36+
"retention period on %s already set to %s",
37+
log_group_name,
38+
current_retention,
39+
)
2640

2741

2842
def handle(request={}, context={}):
29-
log.info('setting default retention period on newly created log groups')
30-
default_retention_period = int(os.getenv('DEFAULT_RETENTION_PERIOD_IN_DAYS', '30'))
31-
set_log_retention(default_retention_period)
43+
dry_run = request.get("dry_run", False)
44+
if "dry_run" in request and not isinstance(dry_run, bool):
45+
raise ValueError(f"'dry_run' is not a boolean value, {request}")
3246

47+
default_log_retention = int(os.getenv("DEFAULT_LOG_RETENTION_IN_DAYS", "30"))
48+
days = request.get("days", default_log_retention)
49+
if "days" in request and not isinstance(days, int):
50+
raise ValueError(f"'days' is not a integer value, {request}")
3351

52+
set_log_retention(days, dry_run)

0 commit comments

Comments
 (0)