CloudWatch Logsからステータスコード404のレポートを作成する方法(Python + Boto 3)

※PHP版も掲載しました!!(PHPを使ってCloudWatch Logsからデータを取得し、4xx系のレポートを作成する方法

CloudWatch Logsを導入するとAWSにログを保存することができます。この記事では、AWS上に保存したログを取得・加工してレポートを作成する方法を紹介します。

作成するレポートは、ステータスコード4xx系が発生したパスとその発生回数をまとめたものになります。フォーマットは下の例のように、パスと回数を1行で表示しています。また、発生回数の多いもの順にソートしています。

/foo.php: 50
/bar.php: 40
/baz.php: 35

なお、レポートの取得や整形はPython(3.5.1)を使い、AWSへのアクセスはBoto 3を利用します。

また、今回のサンプルではレポートをファイルに保存していますが、実際の運用上はメールで送信する方が望ましいと思います。

CloudWatch Logsにログを保存する方法は、AWSのドキュメントを読むのがいいと思います。また、私の過去の記事CloudWatch Logsを使って500系のレスポンスを検知する方法も参考になれば幸いです。

CLIでCloudWatch Logsからデータを取得

filter-log-eventsを叩く

Boto 3を使ってCloudWatch Logsにアクセスする前に、CLIを使ってコマンドライン城からデータを取得できるかを確かめてみます。使用するコマンドは、aws logsの、filter-log-eventsです。

filter-log-eventsにlog-group-namestart-timeend-timeを指定して、以下のようにデータを取得することができます。例えば、log-group-nameが「LOG_GROUP_NAME」で、2016年1月1日から2016年1月2日までのログを取得する場合は、以下のコマンドで実現できます。

$ aws logs filter-log-events \
  --log-group-name LOG_GROUP_NAME \
  --start-time 1451574000000 \
  --end-time 1451660400000

ちなみに、log-group-nameは、CloudWatch Logsの設定ファイル/etc/awslogs/awslogs.confに、以下のようなに定義したものを指定します。

[/var/log/nginx/access.log]
...
log_group_name = LOG_GROUP_NAME

また、start-timeとend-timeは、タイムスタンプをミリ秒に直したものを使用しています。

A point in time expressed as the number of milliseconds since Jan 1, 1970 00:00:00 UTC. If provided, events with a timestamp prior to this time are not returned.

※上記のように、AWSのドキュメントにもミリ秒に直せとあります。

取得結果は、JSON形式の場合、以下のように取得することができます。

{
    "searchedLogStreams": [
        {
            "searchedCompletely": true,
            "logStreamName": "i-xxxxxxxx"
        }
    ],
    "events": [
        {
            "ingestionTime": 1451613286668,
            "timestamp": 1451613281122,
            "message": "xxx.xxx.xxx.xxx - - [01/Jan/2016:10:54:40 +0900] \"GET /blog/2764/ HTTP/1.1\" 200 ...",
            "eventId": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
            "logStreamName": "i-xxxxxxxx"
        },
    ]
}

IAMでポリシーを追加

※2016年1月6日 13:46分ごろに追記しました。

filter-log-eventsを叩くために、IAMを使ってCloudWatch Logsにアクセスするためのポリシーを追加する必要があります。

以下のようにポリシーを書いたり、既存のポリシーloudWatchLogsReadOnlyAccessを追加するなどで、filter-log-eventsを利用できるようになります。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "logs:FilterLogEvents"
            ],
            "Resource": [
                "*"
            ]
        }
    ]
}

フィルタリング

filter-log-eventsコマンドは、filter-patternオプションを使うことで、例えばステータスコードが4xxだけ取得するなど、フィルタリングした結果のみを取得することができます。

filter-patternは、CloudWatch Logsの「Metric Filter」で指定する「Filter Pattern」ような形式になります。例えば、私の環境のアクセスログだと、以下のようにfilter-patternを指定すると、4xx系のログのみを抽出することができます。

$ aws logs filter-log-events \
  --log-group-name LOG_GROUP_NAME \
  --start-time 1451574000000 \
  --end-time 1451660400000 \
  --filter-pattern "[ip, dummy1, uid, timestamp, request, status_code = 4*, bytes, referer, user_agent]"

Pythonでアクセスレポートを作成

Boto 3をインストール

pipを使うとBoto 3を簡単にインストールできます。

$ pip install boto3

Boto 3でCloudWatch Logsからデータを取得

boto3.client('logs')でクライアントを作成し、filter_log_eventsメソッドを呼ぶだけでCloudWatch Logsからデータを取得することができます。

例えば、先ほどCLIを使った時と同じ条件でデータを取得する場合は、Boto 3を使って以下のように書くことができます。

import boto3
client = boto3.client('logs')
params = {
    'logGroupName': 'LOG_GROUP_NAME',
    'filterPattern': '[ip, dummy1, uid, timestamp, request, status_code = 4*, bytes, referer, user_agent]',
    'startTime': 1451574000000,
    'endTime': 1451660400000,
}
response = client.filter_log_events(**params)

これを実行すると、responseにJSON形式のデータが格納されます。

アクセスレポートを作成するコード

これらをふまえ、以下のコードで4xx系が発生したパスと、その回数をまとめたレポートを出力するコードです。

この記事が役に立った場合、シェアしていただけると励みになります!!