every Tech Blog

株式会社エブリーのTech Blogです。

Amazon CloudWatch Logs Insights を使ったログ調査〜最新機能を添えて

Amazon CloudWatch Logs Insights を使ったログ調査〜最新機能を添えて
Amazon CloudWatch Logs Insights を使ったログ調査〜最新機能を添えて

 こんにちは、開発本部 RetailHUB開発部 NetSuperグループ兼、CTO 室 Dev Enable グループに所属するフルスタックエンジニアをやらせていただいています、ホーク🦅アイ👁️です。AWS Summit2025が来月に開催されますがいかがお過ごしでしょうか。

aws.amazon.com

まえがき

 弊社ではつい先だってプレスリリースされました、Cursorエディタの全エンジニア配布が社内で最もインパクトのある出来事となっており開発部内でもSlackで専用のチャンネルを用意して会話をしたり、社内勉強会の開催も近々予定していたりと大いに盛り上がっております。

prtimes.jp

背景

 昨年、私が執筆したブログ記事でAmazon Data Firehoseについて触れさせていただきましたが、データウェアハウスなログ管理・分析手法として現在も所属チームで稼働中です。

tech.every.tv

一方、別のインフラ構成で稼働しているGraphQLシステムがありこちらはAWS Lambdaを導入しているだけでそのログは標準のAmazon CloudWatch Logsに全依存しているのが現状です。そんな中、とある調査が必要になりこのLambdaのロググループの大量のログを検索しなければならないことになったのです。構造化ログと非構造化ログが混在しておりS3に保存もしていないので過去のログを今すぐにAthenaを使ってクエリ発行もできないため仕方なくLogs Insightsを使って直接検索することになりました。Logs Insights自体は2018年にGAとなっているレガシー技術ですが、実は直近で大きな新しい機能が追加となっていたのでそれを紹介しつつ、本調査の体験談をさせたいただきます。

Cloud Watch Logs Insightsを使った調査事例

 先ほど述べたLambdaログを調査するにあたり、現状システムのログ出力前提は以下のようになっています。

  • APIプログラム内で出力されるログの種類は、処理途中の変数値やタイムスタンプを記したinfoログと例外発生時のエラーログがあり、その2種類が同じロググループに混在している(構造化・非構造化ログの混在)
  • 調査対象のAPIはログイン後に特定のユーザ(スマホアプリ)が叩くものだが一部のログにしかユーザIDが埋め込まれていない
  • エラーログにはそのユーザIDは埋め込まれていない
  • STG環境とPRD環境のログが同じロググループに混在している

 この前提条件の下、1リクエストで出力させたログをまとめて、ある期間中にどのユーザが特定エラーを起こしたのか、またその件数はいくつなのか、何人がエラーを起こしたのかを抽出したいという要件がありました。

 これらを踏まえてChatGPTと壁打ちの結果、なんとかLogs Insights QLのクエリ構文を完成させることができ、期待するデータを抽出することができました。クエリ例は以下のようになります。

fields @timestamp, @message
| parse @message "*\t*\t*\t*" as level, timestamp, request_id, body
| parse body /'user_id':\s*'(?<usr_id>[^']+)'/
| parse body /'shop_id':\s*'(?<shp_id>[^']+)'/
| parse body /'db_name':\s*'(?<db_nme>[^']+)'/
| parse body /'error_message':\s*'(?<err_message>[^']+)'/
| stats latest(usr_id) as user_id,
     latest(shp_id) as shop_id,
     latest(db_nme) as db_name,
     latest(err_message) as error_message,
     latest(timestamp) as log_timestamp
     by request_id
| filter error_message = "情報が取得できませんでした。" and db_name != "stg_database"

 簡単に説明すると、@messageでログ文字列全体を取得して、タブ区切りになっている部分を4分割してLambdaのリクエストを一意に決めるRequestIdを取得しつつ、ログ本体をbodyとしています。このRequestIdでグルーピングすることで1リクエストのログ全体を集約しました。ログは、plaintextの場合もあればJson形式の場合もあったり、またJson形式がネストしていたりとJsonパースを共通ルール化して記述できなかったので単純に正規表現でuser_idなどを抽出しています。最後にPRD環境のみとする条件を追加しています。

最新のCloud Watch Logs Insights事情

 CloudWatch Logs では2024年12月、以下の3つの機能がGAとなって可能性が広がっております。

  • 生成AIを用いた自然言語によるクエリ作成・更新機能
  • OpenSearch Service Piped Processing Language (PPL)のサポート
  • OpenSearch Service Structured Query Language (SQL)のサポート

 また、2025年7月31日以降は、IAMポリシーでlogs:StartQuery,logs:GetQueryResultsの権限追加が必須となりセキュリティ向上が見込まれます。

 そこで、先ほどのLambdaログ調査で使ったクエリと同様の結果を出すためのSQL記述はどのようなものなのか、生成AIプロンプトでリクエストするとどのようなクエリを生成するのかも気になりました。もしかしたら、もっと簡単な文法で記述できていたのだろうか、GPTとの壁打ちをしてやっとの思いで完成させたクエリだったのでその時間短縮ができるのであれば良い選択肢になると思い試すことにしました。

新機能1:AIアシスタントによるクエリ自動生成

日本語プロンプトの制限事項

 Cursorに上記の完成形のクエリを渡して「このクエリ結果と同じになるクエリを生成してもらうためのプロンプトを考えて」とお願いして作成してもらったものを使って実行してみたら以下のエラーになりました。日本語がそもそもダメだったようです。。

# 日本語での入力例(エラーになります)
Lambda関数のログから以下の条件で分析をしたいです:
1. ログからuser_id, shop_id, db_name, error_messageを抽出
2. request_idでグループ化し、各フィールドの最新値を取得
3. error_messageが"情報が取得できませんでした。"で、
   db_nameが"stg_database"以外のものを抽出

⚠️ エラーメッセージ

Bad Request - Query generation currently only supports prompts in English. 
Only english characters, numbers and standard punctuations are allowed.

英語プロンプトの例(日本語文字列あり:エラー発生)

 日本語には対応していないようなので、英語プロンプトに変更してとCursorにお願いして作成し直してもらいました。それを使って実行するとまたもやエラーになってしまいました。。どうやら、メッセージ文字列そのものも日本語(マルチバイト?)が混じっているとエラーになるようです。

I want to analyze Lambda function logs with the following conditions:
1. Extract user_id, shop_id, db_name, and error_message from logs
2. Group by request_id and get the latest value for each field
3. Filter where error_message is "情報が取得できませんでした。"
   and db_name is not "stg_database"

⚠️ エラーメッセージ

Bad Request - Query generation currently only supports prompts in English. 
Only english characters, numbers and standard punctuations are allowed.

複雑なプロンプト例(エラー発生)

 というわけで全て英語のプロンプトをCursorにお願いして作成してもらい、それを使って実行してみましたが、以下のようにまたまたエラーになってしまいました。長文だったり要件が複数・複雑だとエラーが出てクエリ生成に失敗してしまったのでそもそも簡単なクエリ生成にしか対応できないようです。

I want to analyze Lambda function logs with the following conditions:
1. Extract user_id, shop_id, db_name, and error_message from logs
2. Group by request_id and get the latest value for each field
3. Filter where error_message is "Information could not be retrieved"
   and db_name is not "stg_database"

⚠️ エラーメッセージ

Bad Request - The provided query prompt failed to generate a valid query. 
Try rephrasing the query prompt.

 その後、簡単な文章プロンプトで少しづつクエリを組み立ていこうと、Generate New Query→Update Query→Update Query...と繰り返していきましたが単純にUpdateでプロンプトの結果を追加する時もあれば上書きする時もあったので中々一筋縄ではいかない結果となりました。。

新機能2:OpenSearchSQLクエリ

 こちらも同様にCursorを使ってSQLを作ってもらってそれを実行する方法で試しました。確かに簡単なSQL構文では正しく動きましたし、元からある知見をそのまま使ってすぐにクエリ作成ができるのは便利でした。しかし、今回のようなちょっと複雑なクエリ要件になるとCursorが生成したSQL(以下、一例)では文法NGだったり実行できてもヒット数0件だったりと何度か壁打ちして試しましたが上手くいきませんでした。。

SELECT 
    request_id,
    user_id,
    shop_id,
    db_name,
    error_message,
    `@timestamp` as log_timestamp
FROM 
    `/aws/lambda/FunctionA`
WHERE 
    regexp_extract(`@message`, 'error_message''?\s*:\s*''([^'']+)''', 1) = '情報が取得できませんでした。'
    AND regexp_extract(`@message`, 'db_name''?\s*:\s*''([^'']+)''', 1) != 'stg_database'
| fields
    regexp_extract(`@message`, 'user_id''?\s*:\s*''([^'']+)''', 1) as user_id,
    regexp_extract(`@message`, 'shop_id''?\s*:\s*''([^'']+)''', 1) as shop_id,
    regexp_extract(`@message`, 'db_name''?\s*:\s*''([^'']+)''', 1) as db_name,
    regexp_extract(`@message`, 'error_message''?\s*:\s*''([^'']+)''', 1) as error_message,
    split(`@message`, '\t')[3] as request_id,
    `@timestamp`
| sort `@timestamp` desc
| limit 1000;

総括

 本記事では、前半でCloudWatch Logs Insightsを使ったログ調査事例について話しました。後半では、最新機能についても触れました。生成AIによる自動生成はまだまだ改善の余地がありそうな結果でした。SQLの方は構文や関数は既に出揃っているのでそれらを全て理解すれば複雑なクエリ実行も成功することはできると思います。

 そもそも、Logs Insights使用はマネーコストが掛かるためこれだけを使ったログ管理運用は最適解ではないかもしれません。そのため使用頻度は高くないと思いますが、今回のようなデータレイクないろんな形式のログが混在しているのを一括して検索可能であったり、複数のLogGroupsを横断して検索可能であったり、CloudWatch Dashboardsでリアルタイム監視ができたり、と使い分けとして使用機会自体はまだまだありますので新しい機能を駆使して快適なログライフを送れると良いと思いました。

 また、新しいログ分析手法としてデータレイクとしてApache IcebergをサポートしたS3 Tablesを使ってAthenaで分析するパターンが出てきました。2025年5月執筆時点ではコンソール上でnamespace作成、テーブル作成も可能となっておりました。こちらは既存の弊社システムのログ分析手法をアップデートでき得るものとして後日検証して別途報告できればと思います。

 これにて本記事の結びとさせていただきます。

最後に

エブリーでは、ともに働く仲間を募集しています。

テックブログを読んで少しでもエブリーに興味を持っていただけた方は、ぜひ一度カジュアル面談にお越しください!

corp.every.tv