CloudWatchのログをBoto3(AWS SDK for Python)でローカルファイルに出力してみた

こんにちは、みかみです。

au 金曜日クーポンで、沖縄だったらブルーシールアイスがもらえるんですって(いいなーv

はじめに

AWS管理コンソールからCloudWatchのログ確認してたのですが、長くなるとスクロールするたびロードで待たされてつらい。。

S3にエクスポートできるものの、パーミッション設定必要だったり、gzipなのでWindowsだと地味にめんどくさかったり。。

やりたいこと

  • CloudWatch のログをS3を経由しないでローカルにファイル保存したい
  • boto3 の CloudWatchLogs で取れるものを確認してみたい

前提条件

やってみよう

ロググループを取得

まずは、prefixを指定して、ロググループを取得してみます。

お目当てのログのディレクトリを検索するイメージです。

import boto3

prefix = '/aws/lambda/test'

client = boto3.client('logs')
response = client.describe_log_groups(
    logGroupNamePrefix=prefix
)

print(response)

実行してみると、こんなレスポンスが返ってきました。

{
    'ResponseMetadata': {
        'HTTPHeaders': {
            'content-length': '660',
            'content-type': 'application/x-amz-json-1.1',
            'date': 'Thu, 09 Mar 2017 01:51:02 GMT',
            'x-amzn-requestid': 'da60f054-046a-11e7-bca8-dda7a1313588'
        },
        'HTTPStatusCode': 200,
        'RequestId': 'da60f054-046a-11e7-bca8-dda7a1313588',
        'RetryAttempts': 0
    },
    'logGroups': [
        {
            'arn': 'arn:aws:logs:ap-northeast-1:109054975408:log-group:/aws/lambda/test-get-sqs-message:*',
            'creationTime': 1488869579001,
             'logGroupName': '/aws/lambda/test-get-sqs-message',
             'metricFilterCount': 0,
             'storedBytes': 3300
        },

(中略)

        {
            'arn': 'arn:aws:logs:ap-northeast-1:109054975408:log-group:/aws/lambda/test-sqs-lambda-func:*',
             'creationTime': 1488949191347,
             'logGroupName': '/aws/lambda/test-sqs-lambda-func',
             'metricFilterCount': 0,
             'storedBytes': 0
        }
    ]
}

ログストリームを取得

今度は見つかったロググループを指定して、ログストリームの一覧を取得してみます。

ディレクトリ内のログファイル名を取得するイメージです。

import boto3

group_name = '/aws/lambda/test-get-sqs-message'

client = boto3.client('logs')
response = client.describe_log_streams(
    logGroupName=group_name,
    orderBy='LastEventTime',
    descending=True
)

print(response)

実行します。

{
    'ResponseMetadata': {
        'HTTPHeaders': {
            'content-length': '1865',
            'content-type': 'application/x-amz-json-1.1',
            'date': 'Thu, 09 Mar 2017 02:11:17 GMT',
            'x-amzn-requestid': 'ae3dc71f-046d-11e7-9461-23784152c415'
        },
        'HTTPStatusCode': 200,
        'RequestId': 'ae3dc71f-046d-11e7-9461-23784152c415',
        'RetryAttempts': 0
    },
    'logStreams': [
        {
            'arn': 'arn:aws:logs:ap-northeast-1:109054975408:log-group:/aws/lambda/test-get-sqs-message:log-stream:2017/03/07/[$LATEST]a723d0594e6f41808989fe603b4fd205',
            'creationTime': 1488875487417,
            'firstEventTimestamp': 1488875487686,
            'lastEventTimestamp': 1488875488722,
            'lastIngestionTime': 1488875503816,
            'logStreamName': '2017/03/07/[$LATEST]a723d0594e6f41808989fe603b4fd205',
            'storedBytes': 454,
            'uploadSequenceToken': '49570236197125197928892515127748130759845332711823640194'
        },
        
(中略)
        
        {   'arn': 'arn:aws:logs:ap-northeast-1:109054975408:log-group:/aws/lambda/test-get-sqs-message:log-stream:2017/03/07/[$LATEST]aa2e39e31057441c9547b10f4bae7476',
            'creationTime': 1488869579056,
            'firstEventTimestamp': 1488869579039,
            'lastEventTimestamp': 1488869579705,
            'lastIngestionTime': 1488869594261,
            'logStreamName': '2017/03/07/[$LATEST]aa2e39e31057441c9547b10f4bae7476',
            'storedBytes': 680,
            'uploadSequenceToken': '49568106864214712160805596000628551033956595763377078690'
        }
    ]
}

ログストリーム一覧が取得できました。

ログを取得してjsonファイルに書き出す

ロググループとログストリームを指定して、ログを取得します。

ログファイルを開いて中身を見てみるイメージです。

import boto3

group_name = '/aws/lambda/test-get-sqs-message'
stream_name = '2017/03/07/[$LATEST]a723d0594e6f41808989fe603b4fd205'

client = boto3.client('logs')
response = client.get_log_events(
    logGroupName=group_name,
    logStreamName=stream_name,
    startFromHead=True
)
print(response)

こんなレスポンスが返ってくるので、

{
    'ResponseMetadata': {
        'HTTPHeaders': {
            'content-length': '1311',
            'content-type': 'application/x-amz-json-1.1',
            'date': 'Thu, 09 Mar 2017 02:31:37 GMT',
            'x-amzn-requestid': '855ca9a7-0470-11e7-93ac-912c20609353'
        },
        'HTTPStatusCode': 200,
        'RequestId': '855ca9a7-0470-11e7-93ac-912c20609353',
        'RetryAttempts': 0
    },
    'events': [
        {
            'ingestionTime': 1488875487703,
            'message': 'START RequestId: 74d22ae4-0310-11e7-9f8d-11c318320fa0 Version: $LATEST\n',
            'timestamp': 1488875487686
        },
        
(中略)
        
        {
            'ingestionTime': 1488875503816,
            'message': 'REPORT RequestId: 74d22ae4-0310-11e7-9f8d-11c318320fa0\tDuration: 1034.75 ms\tBilled Duration: 1100 ms \tMemory Size: 128 '
            'MB\tMax Memory Used: 30 MB\t\n',
            'timestamp': 1488875488722
        }
    ],
    'nextBackwardToken': 'b/33203032883223504422259409965453248701700005246959026176',
    'nextForwardToken': 'f/33203032906327076447937135559563551360914771704139022338'
}

ログ本体('events')部分をjsonファイルに書き出しました。

import json

(中略)

with open('log.json', 'w') as f:
    json.dump(response['events'], f, sort_keys=True, indent=4)

できたjsonファイルを見てみます。

[
    {
        "ingestionTime": 1488875487703,
        "message": "START RequestId: 74d22ae4-0310-11e7-9f8d-11c318320fa0 Version: $LATEST\n",
        "timestamp": 1488875487686
    },
    
(中略)
    
    {
        "ingestionTime": 1488875503816,
        "message": "REPORT RequestId: 74d22ae4-0310-11e7-9f8d-11c318320fa0\tDuration: 1034.75 ms\tBilled Duration: 1100 ms \tMemory Size: 128 MB\tMax Memory Used: 30 MB\t\n",
        "timestamp": 1488875488722
    }
]

とりあえず出力してみたものの、なにしろJSONだし、UNIXタイムスタンプだし。。(見にくいし、grepするにも微妙だし。。

なんか、いまいち。。

ログを整形してファイル出力する

ちゃんとログファイル的なものに整形して出力することにします。

ロググループを指定して、すべてのログストリームのログを出力してみます。

import boto3
from datetime import datetime

# ロググループ名
group_name = '/aws/lambda/test-get-sqs-message'

# ログストリーム一覧を取得
client = boto3.client('logs')
response = client.describe_log_streams(
    logGroupName=group_name,
    orderBy='LastEventTime',
    descending=True
)

# 全てのストリームのログを取得
for stream in response['logStreams']:
    stream_name = stream['logStreamName']
    log_timestamp = (datetime.fromtimestamp(int(str(stream['creationTime'])[:10]))).strftime('%Y%m%d%H%M%S')
    # ログファイル名:[ストリーム名]_[作成日時].log(Windowsだとファイル名に '/' を入れられないので取り除く)
    file_name = '{}_{}.log'.format(stream_name, log_timestamp).replace('/', '')

    # ログを取得
    logs = client.get_log_events(
        logGroupName=group_name,
        logStreamName=stream_name,
        startFromHead=True
    )

    # 'timestamp' と 'message' をログファイルに出力
    body = logs['events']
    with open(file_name, 'w') as f:
        for line in body:
            message = '[{}] {}'.format(datetime.fromtimestamp(int(str(line['timestamp'])[:10])), line['message'])
            f.write(message)

ログファイルが出力できました!

[2017-03-07 17:31:27] START RequestId: 74d22ae4-0310-11e7-9f8d-11c318320fa0 Version: $LATEST
[2017-03-07 17:31:28] msg_3
[2017-03-07 17:31:28] msg_1
[2017-03-07 17:31:28] msg_2
[2017-03-07 17:31:28] END RequestId: 74d22ae4-0310-11e7-9f8d-11c318320fa0
[2017-03-07 17:31:28] REPORT RequestId: 74d22ae4-0310-11e7-9f8d-11c318320fa0	Duration: 1034.75 ms	Billed Duration: 1100 ms 	Memory Size: 128 MB	Max Memory Used: 30 MB

おわりに(所感)

今回は全てのログをファイル出力しただけですが、どう処理するかはコードで好きに書けるので、用途に合わせてフィルタかけたり、データベースに出力したり。

Lambdaと連携させれば、ログ監視機能も手軽に実装できちゃいますねv

参考