[アップデート] Amazon Data Firehose に CloudWatch Logs ログイベントからメッセージデータのみを抽出出来るオプションが追加されたので有効にしてみた

2024.02.28

いわさです。

今朝のアップデートで Kinesis Firehose、いや、Amazon Data Firehose で次のアップデートがアナウンスされました。

なんと、CloudWatch Logs + Firehose でログ転送を行う時に、オプションでログのソースレコードを抽出してくれるようになります。

通常特に指定しないと場合は CloudWatch Logs によって次のように構造化されたメッセージにソースメッセージを含める形になります。

{
  "messageType": "DATA_MESSAGE",
  "owner": "550669467088",
  "logGroup": "API-Gateway-Execution-Logs_67by1bnql1/hoge",
  "logStream": "14ff1bdcd999b9026e06c0c7e1fe47d5",
  "subscriptionFilters": [
    "hoge0228default"
  ],
  "logEvents": [
    {
      "id": "38113950268226860994716085213969933470157804499142705152",
      "timestamp": 1709088639367,
      "message": "hogehoge"
    },

:

    {
      "id": "38113950268293763230311677083394540624975749583660646413",
      "timestamp": 1709088639370,
      "message": "fugagufa"
    }
  ]
}

ただし、オリジナルのログレコードのみを転送して Athena などから参照するケースも多く、これまでは次のような方法で Lambda 関数を使ってメッセージ部分のみ抽出する方法がありました。また、そのための関数が Lambda のブループリント「Kinesis Data Firehose Cloudwatch Logs Processor」として提供されていました。

提供されている Lambda 関数を使うと、上記の構造化されたログは次のように処理されます。

hogehoge
fugafuga

今回アップデートされた機能を使うことでおそらく上記 Lambda 関数が不要になりそうです。
検証してみましょう。

検証してみた

次のように適当な API Gateway から CloudWatch Logs へログを出力しておきます。

そうするとあるロググループに次のようにザーッとログが出力されるようになりました。
CloudWatch Logs から分析を行う場合はまぁこのままで良いですよね。Logs Insights を使おうとするともう少し構造化させたい気もしますが。

このロググループのサブスクリプションフィルターに Firehose を 2 つ設定しました。

1つ目のhoge0228defaultは特にオプションを指定せずにそのまま S3 へ出力するものです。
今回追加されたオプションは「レコードを変換および転換 - オプション」エリアにある、「ログイベントからのみメッセージデータを抽出」ですね。
デフォルトではオフとなっており、また選択が出来ないようになっています。
このオプションを使うためには「Amazon CloudWatch Logs からソースレコードを解凍する - 新着」をオンにした上で、追加の有効化が必要です。

2つ目のhoge0228extractでは、前述のオプションを次のように有効化しましょう。

なお、CloudWatch Logs からソースレコードを解凍するというオプションは、CloudWatch Logs から Firehose へ GZip 圧縮されてデータ送信されるのですが、それを Firehose 側で解凍してそれを配信先へ展開出来るオプションです。

2023 年 12 月に追加された新し目のオプションです。今回の追加機能を利用するためにはこの解凍オプションの利用が前提となります。

出力されたログを確認してみる

少し待つと Firehose を経由して S3 バケットへログが出力されます。
出力されたオブジェクトをダウンロードして中身をのぞいてみましょう。

デフォルト

まずはオプションを使わないデフォルトのものから。

デフォルトのものは GZip 圧縮されているので、解凍してから確認しています。

% cat hoge0228default-1-2024-02-28-02-50-57-d74a9480-2626-4ca9-9b57-56bc25b61a5c
��Xmo�8�+�mw-�Ի�(�qS�u���v��"����ZU�J����v��{��&�'�3>��C�%J�8]�h����5N&����1�M����G|?r�����1
[��[L���|���f+:�>�����M�@V=��<���>��K���Q���,IR?t��1d3���km�E^(���ihcĶ��)��5<��Ji����F�pBJ��#6%QHB7$!a�O|�v�E>�l��m��
                                                                                                                     jG�>W�%J����рD$
                                                                                                                                    }�  �l�e�3�9��g�gQj�ĝ�Q���<��(�׵I`{������<�8�s����Q�gLa�E>_��k$B�*�)�r2�B�H9J��jÒv4Ql�=��*��wJ� ��C<?t<?p/r��
                                                    �$�&/�V#������W�@�>5\1(�2W����A��X�e?�0Y�l�Q����]��t��GO���NMJ�6n���_���y\�^N��0n��'�iޠ�HΙ�z�}0b�q��n�u�����0��
�S�bR��Z<

:

_M�4@s9E]W����K�����a��1�K����u�d���E�:x��� Z0h"��UQs�rڐm��8O�*ԭf
                                                                 o��*V�Va�v�
                                                                            ����,9�3|�=��m�����|��U'�c�\Jm�����*�������ۛ�҇���ܛm�*Ģ�����7އ���-�&���z�:`b���?5��{%   

:

% cat hoge0228default-1-2024-02-28-02-50-57-d74a9480-2626-4ca9-9b57-56bc25b61a5c | jq
{
  "messageType": "DATA_MESSAGE",
  "owner": "550669467088",
  "logGroup": "API-Gateway-Execution-Logs_67by1bnql1/hoge",
  "logStream": "25560d0076e205c875b12162bccd6840",
  "subscriptionFilters": [
    "hoge0228default"
  ],
  "logEvents": [
    {
      "id": "38113950210980848070087975606524139960526262171958312960",
      "timestamp": 1709088636800,
      "message": "(beef0eb5-fb9d-404f-97c8-f7630ad386f6) Extended Request Id: T03bjGEkNjMEdvQ="
    },

:

    {
      "id": "38113950211047750305683567475948747115344207256476254221",
      "timestamp": 1709088636803,
      "message": "(beef0eb5-fb9d-404f-97c8-f7630ad386f6) Method completed with status: 200"
    }
  ]
}
{
  "messageType": "DATA_MESSAGE",
  "owner": "550669467088",
  "logGroup": "API-Gateway-Execution-Logs_67by1bnql1/hoge",
  "logStream": "14ff1bdcd999b9026e06c0c7e1fe47d5",
  "subscriptionFilters": [
    "hoge0228default"
  ],
  "logEvents": [
    {
      "id": "38113950268226860994716085213969933470157804499142705152",
      "timestamp": 1709088639367,
      "message": "(58f9789d-913f-44ec-b294-6537473ac7cb) Extended Request Id: T03b8H1-NjMEflw="
    },

:

    {
      "id": "38113950268293763230311677083394540624975749583660646413",
      "timestamp": 1709088639370,
      "message": "(58f9789d-913f-44ec-b294-6537473ac7cb) Method completed with status: 200"
    }
  ]
}

予想どおり JSON 構造のログ形式で出力されていますね。

抽出オプションを使ったもの

こちらは今回のオプションを使ったオブジェクトです。

% cat hoge0228extract-1-2024-02-28-02-49-14-48ee7327-e8ea-449d-b6f3-1b1f383dc9f2
(beef0eb5-fb9d-404f-97c8-f7630ad386f6) Extended Request Id: T03bjGEkNjMEdvQ=
(beef0eb5-fb9d-404f-97c8-f7630ad386f6) Verifying Usage Plan for request: beef0eb5-fb9d-404f-97c8-f7630ad386f6. API Key:  API Stage: 67by1bnql1/hoge
(beef0eb5-fb9d-404f-97c8-f7630ad386f6) API Key  authorized because method 'GET /' does not require API Key. Request will not contribute to throttle or quota limits
(beef0eb5-fb9d-404f-97c8-f7630ad386f6) Usage Plan check succeeded for API Key  and API Stage 67by1bnql1/hoge
(beef0eb5-fb9d-404f-97c8-f7630ad386f6) Starting execution for request: beef0eb5-fb9d-404f-97c8-f7630ad386f6
(beef0eb5-fb9d-404f-97c8-f7630ad386f6) HTTP Method: GET, Resource Path: /
(beef0eb5-fb9d-404f-97c8-f7630ad386f6) Method request path: {}
(beef0eb5-fb9d-404f-97c8-f7630ad386f6) Method request query string: {}
(beef0eb5-fb9d-404f-97c8-f7630ad386f6) Method request headers: {sec-fetch-mode=navigate, sec-fetch-site=none, accept-language=en-US,en;q=0.9,ja;q=0.8, cookie=AMCV_7742037254C95E840A4C98A6%40AdobeOrg=1585540135%7CMCIDTS%7C19726%7CMCMID%7C13953826734270235694244559878765585431%7CMCAID%7CNONE%7CMCOPTOUT-1704354506s%7CNONE%7CvVersion%7C4.4.0%7CMCAAMLH-1704952106%7C11%7CMCAAMB-1704952106%7Cj8Odv6LonN4r3an7LhD3WZrU1bUpAkFkkiY1ncBR96t2PTI; _gcl_au=1.1.691424627.1708062998; _ga=GA1.1.714958343.1708062998; _ga_5D40Y9CZ1M=GS1.1.1708062998.1.0.1708062999.59.0.0, User-Agent=Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36, X-Forwarded-Proto=https, Host=67by1bnql1.execute-api.ap-northeast-1.amazonaws.com, sec-fetch-user=?1, X-Forwarded-Port=443, X-Amzn-Trace-Id=Root=1-65de9f7c-49d770305b60a7016eb4ed44, accept=text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7, sec-ch-ua="Chromium";v="122", "Not(A:Brand";v="24 [TRUNCATED]
(beef0eb5-fb9d-404f-97c8-f7630ad386f6) Method request body before transformations:

メッセージデータのみが抽出されていることで、オリジナルのログがそのまま出力されるようになっていますね。

今回のオプション自体は無料だが、別の有料オプションの利用が前提となる

アップデートアナウンスには次のように記載されています。

There is no additional charge to apply message extraction when customers use Firehose decompression for CloudWatch Logs.

無料で利用出来るように読み取れますが、前提条件として CloudWatch Logs からのソースレコード解凍機能オプションをオンにする必要があって、そちらはオプション料金が発生するので注意しましょう。
処理するログの量に応じた従量課金となっています。

Decompression for CloudWatch Logs, per GB decompressed: $0.00403

さいごに

本日は Amazon Data Firehose に CloudWatch Logs ログイベントからメッセージデータのみを抽出出来るオプションが追加されたので有効にしてみました。

Lambda を用意するより楽ですね。
アナウンスだけ見ると追加料金なしで利用出来るように読み取れるのですが、現在解凍オプションを使っていない場合はその分の料金は発生するので注意しましょう。