Created
January 21, 2019 16:26
-
-
Save shouptech/174d545a0302ebcdd8247ae31b1c2da5 to your computer and use it in GitHub Desktop.
Delete empty log streams from AWS CloudWatch Logs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
""" | |
This script deletes empty log streams from cloudwatch log groups. | |
Your shell should be configured for connecting to the AWS API. This can be done | |
with the CLI command `aws configure`. | |
This script requires Python 3.6 or newer, and you must have boto3 installed. | |
""" | |
import argparse | |
import os | |
import boto3 | |
LOG_ONLY = False | |
def get_log_groups(): | |
"""Return a list containing the names of all log groups.""" | |
client = boto3.client('logs') | |
found_all = False | |
next_token = None | |
group_names = [] | |
while not found_all: | |
if next_token is not None: | |
response = client.describe_log_groups(nextToken=next_token) | |
else: | |
response = client.describe_log_groups() | |
if 'logGroups' in response: | |
for group in response['logGroups']: | |
if 'logGroupName' in group: | |
group_names.append(group['logGroupName']) | |
next_token = response.get('nextToken', None) | |
if next_token is None: | |
found_all = True | |
return group_names | |
def get_log_streams(group_name): | |
"""Return a list of log stream names in group `group_name`.""" | |
client = boto3.client('logs') | |
found_all = False | |
next_token = None | |
stream_names = [] | |
while not found_all: | |
if next_token is not None: | |
response = client.describe_log_streams(logGroupName=group_name, | |
nextToken=next_token) | |
else: | |
response = client.describe_log_streams(logGroupName=group_name) | |
if 'logStreams' in response: | |
for stream in response['logStreams']: | |
if 'logStreamName' in stream: | |
stream_names.append(stream['logStreamName']) | |
next_token = response.get('nextToken', None) | |
if next_token is None: | |
found_all = True | |
return stream_names | |
def stream_has_events(group_name, stream_name): | |
"""Return whether or not `stream_name` in `group_name` has any events.""" | |
client = boto3.client('logs') | |
# Return the first event. | |
response = client.get_log_events(logGroupName=group_name, | |
logStreamName=stream_name, | |
limit=1) | |
if 'events' in response and len(response['events']) > 0: | |
return True | |
return False | |
def get_group_empty_streams(group_name): | |
"""Return a list of streams that are empty in `group_name`.""" | |
streams = get_log_streams(group_name) | |
empty_streams = [] | |
for stream in streams: | |
if not stream_has_events(group_name, stream): | |
empty_streams.append(stream) | |
return empty_streams | |
def del_empty_stream(group_name, stream_name): | |
"""Delete `stream_name` from `group_name`.""" | |
client = boto3.client('logs') | |
client.delete_log_stream(logGroupName=group_name, | |
logStreamName=stream_name) | |
def del_empty_streams(): | |
"""Finds all empty streams and deletes them. | |
If the global variable `LOG_ONLY` is True, streams are only logged to the | |
console and not deleted. | |
""" | |
for group in get_log_groups(): | |
print(f'Finding empty streams in group `{group}`') | |
empty_streams = get_group_empty_streams(group) | |
for stream in empty_streams: | |
if LOG_ONLY: | |
print( | |
f'Stream `{stream}` in group `{group}` is empty. ' | |
'Would be deleted...' | |
) | |
else: | |
print( | |
f'Stream `{stream}` in group `{group}` is empty. ' | |
'Deleting...' | |
) | |
del_empty_stream(group, stream) | |
def main(): | |
"""Main handler for this script.""" | |
parser = argparse.ArgumentParser(description='Delete empty log streams.') | |
parser.add_argument( | |
'--log-only', | |
action='store_true', | |
help="If specified, only log to console, don't actually delete.") | |
args = parser.parse_args() | |
global LOG_ONLY | |
LOG_ONLY = args.log_only | |
del_empty_streams() | |
if __name__ == "__main__": | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Here's a CloudFormation template that provisions a Lambda function to each night delete all empty LogStreams for LogGroups with retention settings : https://github.com/gene1wood/delete-empty-cloudwatch-logstreams
This is nice as all you have to do is deploy the CloudFormation stack and it takes care of it from that point on.