[小ネタ] CloudWatch Logsのログイベント総数をカウントしてみた

AWSを愛する皆さま、こんにちは。
最近身体を鍛えたいと思っているコンサルティング部の西野(@xiye_gen)です。

ログイベント総数を数える方法

CloudWatch LogsのAPIを利用します。Boto3を使って実装してみました。

サンプルコード

import boto3
import time

# 13桁のunixtime(msec)でqueryの範囲を指定
START_TIME = 1569855600000 # 2019/10/01 00:00:00
END_TIME = 1572533999000 # 2019/10/31 23:59:59

# クエリごとの実行待ち時間(sec)
WAIT_TIME_PER_QUERY = 1

client = boto3.client('logs')
describe_log_groups_paginator = client.get_paginator('describe_log_groups')

def generate_log_group_list(paginator):
    page_iterator = paginator.paginate()
    log_group_list = []
    for page in page_iterator:
        for log_group in page['logGroups']:
            log_group_name = log_group['logGroupName']
            log_group_list.append(log_group_name)
    print('Number of log groups: {}'.format(len(log_group_list)))
    print(log_group_list)
    return log_group_list


def start_query(log_group):
    response = client.start_query(
        logGroupName=log_group,
        startTime=START_TIME,
        endTime=END_TIME,
        queryString='stats count(*)',
        limit=10000
    )
    return response


def get_query_results(query_id):
    response = client.get_query_results(
        queryId=query_id
    )
    return response


def count_log_events(log_group_list):
    query_id_list = []
    total_number_of_log_events = 0

    for i in range(len(log_group_list)):
        print('Querying [{}] ({}/{})'.format(log_group_list[i], i + 1, len(log_group_list)))
        response = start_query(log_group_list[i])
        time.sleep(WAIT_TIME_PER_QUERY)
        query_id_list.append(response['queryId'])
    
    print('Waiting for query completion.')
    time.sleep(10)

    for i in range(len(query_id_list)):
        results = get_query_results(query_id_list[i])
        number_of_log_events = results['results'][0][0]['value']
        print('{}:{} ({}/{})'.format(query_id_list[i], number_of_log_events, i + 1, len(log_group_list)))
        total_number_of_log_events += int(number_of_log_events)
    
    return total_number_of_log_events


log_group_list = generate_log_group_list(describe_log_groups_paginator)
total_number_of_log_events = count_log_events(log_group_list)

print('Total number of log events: {}'.format(total_number_of_log_events))

変数

変数名 内容
START_TIME クエリ範囲の開始時刻(13桁のunixtime)
END_TIME クエリ範囲の開始時刻(13桁のunixtime)
WAIT_TIME_PER_QUERY クエリごとの実行後の待ち時間(sec)

CloudWatch Logs Insightのクエリ同時実行数がデフォルトで最大4であるため、time.sleep(WAIT_TIME_PER_QUERY)で制限をかけます。
実行時にaccount maximum query concurrency limit of 4 reachedというエラーが出た場合はWAIT_TIME_PER_QUERYの値を増やしてください。

解説

  • describe_log_groups()のpaginatorでロググループ名のリストを作成
  • 各ロググループに対してクエリ(stats count(*))を実行し、クエリIDのリストを作成
  • クエリIDをもとに結果の問い合わせをし、各ロググループにおけるログイベント数を合計

やってみた

事前準備

あらかじめアカウント内に3600個のログイベントを作成します。

  • ロググループを60個作成(log_group_001〜log_group_060)
  • 各ロググループ内にログストリームを60個作成
  • 各ログストリームに一つのログイベントを作成

実行結果

$ python count_log_events.py
Number of log groups: 60
['log_group_001', 'log_group_002', 'log_group_003', 'log_group_004', 'log_group_005', 'log_group_006', 'log_group_007', 'log_group_008', 'log_group_009', 'log_group_010', 'log_group_011', 'log_group_012', 'log_group_013', 'log_group_014', 'log_group_015', 'log_group_016', 'log_group_017', 'log_group_018', 'log_group_019', 'log_group_020', 'log_group_021', 'log_group_022', 'log_group_023', 'log_group_024', 'log_group_025', 'log_group_026', 'log_group_027', 'log_group_028', 'log_group_029', 'log_group_030', 'log_group_031', 'log_group_032', 'log_group_033', 'log_group_034', 'log_group_035', 'log_group_036', 'log_group_037', 'log_group_038', 'log_group_039', 'log_group_040', 'log_group_041', 'log_group_042', 'log_group_043', 'log_group_044', 'log_group_045', 'log_group_046', 'log_group_047', 'log_group_048', 'log_group_049', 'log_group_050', 'log_group_051', 'log_group_052', 'log_group_053', 'log_group_054', 'log_group_055', 'log_group_056', 'log_group_057', 'log_group_058', 'log_group_059', 'log_group_060']
Querying [log_group_001] (1/60)
Querying [log_group_002] (2/60)
Querying [log_group_003] (3/60)
Querying [log_group_004] (4/60)
(中略)
Querying [log_group_057] (57/60)
Querying [log_group_058] (58/60)
Querying [log_group_059] (59/60)
Querying [log_group_060] (60/60)
Waiting for query completion.
2b91099b-230b-48f4-a932-5396b3c88dff:60 (1/60)
a8d92b0a-4a63-4fe5-ad98-e61ca52d3bef:60 (2/60)
1292bda9-e9ee-49d6-b52b-e115bb701740:60 (3/60)
f045733f-042f-4cdb-954b-8e737ee7ecec:60 (4/60)
(中略)
00cde834-7cb4-447f-9904-eba07c61ab7e:60 (57/60)
3f794951-6916-44f0-98ef-796e4369d341:60 (58/60)
f9726b98-5072-41a4-9388-15967abab4dc:60 (59/60)
aa7b27ad-fe59-4e18-a730-df5baee2971a:60 (60/60)
Total number of log events: 3600

ログイベントの総数を取得できました。

注意点

APIの仕様上、単一のクエリから返されるログイベントの最大数は 10000 です。 本エントリに掲載したサンプルコードの場合、クエリ:ロググループの関係が1:1となっているため、ロググループ内のログイベント数が10000を超えた場合正確な総数を取得できません。

参考:Amazon CloudWatch Logs API Reference / StartQuery

終わりに

このブログがほんの少しでも世界を良くできれば嬉しいです。
コンサルティング部の西野(@xiye_gen)がお送りしました。