CloudWatch Logsのメトリクスフィルターの書き方を整理してみた

記事タイトルとURLをコピーする

技術5課の山﨑です。今月から大阪で勤務になりました。やっぱり地元は落ち着きますね。

前回投稿させて頂いたCloudWatch Logsでメトリクスフィルターを作成し、アラームでメール通知させてみたという記事の中でCloudWatchのメトリクスフィルターのフィルターパターンの書き方についてあまり触れていなかったので、自分の頭を整理するという意味でさくっとまとめてみました。

詳細はドキュメントをご覧ください。

メトリクスフィルターの構文

CloudWatch Logsのメトリクスフィルターでは指定された一致する語句や値をログイベントの中で検索できます。例えると正規表現を使った文字列のマッチングのようなイメージです。まずフィルターをかけるログには2つの形式があります。1つはJSON形式のログ、もう1つはスペース区切りのログです。以降、それぞれのログ形式に分けてフィルターパターンの書き方を整理します。

JSON形式のログ

JSON形式のログは下記のようなログです(AWSのドキュメントに筆者加筆)

参照元:フィルターとパターンの構文 - JSON メトリクスフィルターの例

{
  "eventType": "UpdateTrail",
  "sourceIPAddress": "111.111.111.111",
  "arrayKey": [
        "value",
        "another value"
  ],
  "objectList": [
       {
         "name": "a",
         "id": 1
       },
       {
         "name": "b",
         "id": 2
       }
  ],
  "serverworks": {
      "people": {
          "employee": "yamasaki",
          "employee:ID": "12345"
       }
   },
  "SomeObject": null,
  "ThisFlag": true
}

メトリクスフィルターではマッチさせたい文字列の表現方法は大きく分けて3つあります。

ログイベントの語句の一致(文字列一致)

例えば、上記のログから「"eventType": "UpdateTrail"」のログを抽出したい場合は下記のようなフィルターパターンで表現します。

"\"eventType\": \"UpdateTrail\""

この方法はアルファベット文字およびアンダースコア以外の文字を含むメトリクスフィルターの語句は二重引用符 ("") で囲む必要があります。つまり("") はこのフィルターにおいて特殊文字としての役割を果たしているので「\(バックスラッシュ)」で文字をエスケープする必要があります。

JSONログイベントの語句の一致

例えば、ログから「"eventType": "UpdateTrail"」を含むログを抽出したい場合は下記のようなフィルターパターンで表現します。

{ ( $.eventType=UpdateTrail ) }

含む、ではなく除外したい場合は下記のように表現します。(「!」が否定を表しています)

{ ( $.eventType!=UpdateTrail ) }

配列の中にある値を参照したい場合は下記のように表現します。

{ ( $.arrayKey[0]="value" ) }

nullに設定されている「SomeObject」を抽出したい場合は下記のように表現します。

{ ( $.SomeObject IS NULL ) }

OR(||)とAND(&&)を使って複数条件を組み合わせることも可能です。

{ ( $.arrayKey[0]="value" ) && ( $.arrayKey[1]="another value") }
{ ( $.arrayKey[0]="value" ) || ( $.arrayKey[1]="another value") }

"employee": "yamasaki"を抽出したい場合は下記のように表現します。 ※入れ子構造の場合は、「.(ドット)」で変数を繋げます。

{ ( $.serverworks.people.employee=yamasaki ) }

"employee:ID":"12345"を抽出したい場合は下記のように表現します。

"\"employee:ID\":\"12345\""

あれ?これはログイベントの語句の一致(文字列一致)のパターンですね。

はい、その通りです。実は今回はJSON形式では表現できません。JSON形式のログのフィルターパターンは英数字の文字列とハイフン(-)とアンダーバー(_)のみがサポートされているので、「:(コロン)」をプロパティに指定することはできません。よってJSON形式ではなく、ログイベントの語句の一致(文字列一致)のパターンで表現する必要があります。ただし、ログ中の意図した位置以外にも一致する文字列が存在する場合は合わせて抽出されてしまうので注意する必要があります。

スペース区切りのログ

AWSのドキュメントそのままですが、スペース区切りのログは下記のようなログです。 参照元:フィルターとパターンの構文 - スペース区切りログイベントから値を取得するメトリクスフィルターの使用

127.0.0.1 - frank [10/Oct/2000:13:25:15 -0700] "GET /apache_pb.gif HTTP/1.0" 200 1534
127.0.0.1 - frank [10/Oct/2000:13:35:22 -0700] "GET /apache_pb.gif HTTP/1.0" 500 5324
127.0.0.1 - frank [10/Oct/2000:13:50:35 -0700] "GET /apache_pb.gif HTTP/1.0" 200 4355
スペース区切りのログイベントから値を取得

まず、スペース区切りで表現されている値のフィールドが何かを把握しておきます。 上記の例だと [ip, user, username, timestamp, request, status_code, bytes] の順番にフィールドの値がログとして出力されています。

左から順にip, user, username, timestamp, request, status_code, bytes
127.0.0.1 - frank [10/Oct/2000:13:25:15 -0700] "GET /apache_pb.gif HTTP/1.0" 200 1534
127.0.0.1 - frank [10/Oct/2000:13:35:22 -0700] "GET /apache_pb.gif HTTP/1.0" 500 5324
127.0.0.1 - frank [10/Oct/2000:13:50:35 -0700] "GET /apache_pb.gif HTTP/1.0" 200 4355

「status_codeが200」のログを抽出したい場合は下記のように表現します。

[ip, user, username, timestamp, request, status_code = 200, bytes]

「status_codeが200」且つ「4355bytes以上」のログを抽出したい場合は下記のように表現します。

[ip, user, username, timestamp, request, status_code = 200, bytes>= 4355]

ということで少し長くなりましたが、CloudWatchのメトリクスフィルターのフィルターパターンの書き方について整理してみました!