CloudWatch Logs でのログ検索方法を調べてみた

2023.04.25

こんにちは。
ご機嫌いかがでしょうか。
"No human labor is no human error" が大好きな吉井 亮です。

アプリケーションが出力するログはどちらに保管していますでしょうか?
いろいろな選択肢はあると思いますが、CloudWatch Logs が一般的だと思います。(きちんと調べてないですが。)

本エントリではそんな CloudWatch Logs の検索方法を考えてみました。

料金

まずはおさらいとして、CloudWatch Logs の料金を調べてみます。
Amazon CloudWatch 料金表

2023年4月13日時点の東京リージョンでの料金です。

無料利用枠

5GB までは収集、保存、分析の料金はかかりません。

ログ

収集、保存、分析ごとに以下の料金が発生します。

対象 料金
収集 (データの取り込み) 0.76USD/GB
保存 (アーカイブ) 0.033USD/GB
分析 (Logs Insights のクエリ) スキャンしたデータ 1 GB あたり 0.0076USD

Vended Logs

AWS サービスが発行するログです。以下のサービスログが含まれます。

  • Amazon VPC フローログ
  • Amazon Route 53 パブリック DNS クエリログ
  • Amazon Route 53 Resolver クエリログ
  • Amazon ElastiCache for Redis ログ
  • API Gateway Access ログ
  • AWS Step Functions Express と Standard ログ
  • Managed Streaming for Kafka (MSK) ログ
  • Web Application Firewall (WAF) ログ
  • AWS Network Firewall アラートとフローログ
  • Storage Gateway File Audit と Health ログ
  • Chime メディアクオリティメトリクスと Session Initiation Protocol メッセージログ
  • Evidently ログ
  • SageMaker ログ
  • AWS Elemental MediaTailor ログ

こちらはボリュームディスカウントが設定されています。

取り込まれたデータ 料金
最初の 10 TB まで 0.76USD/GB
10~30 TB 0.38USD/GB
30~50 TB 0.152USD/GB
50TB 以降 0.076USD/GB
保存されたデータ 0.033USD/GB

検索その1 マネジメントコンソール

それでは、ここから検索方法を紹介します。

ログイベント検索

マネジメントコンソールで CloudWatch Logs を開きます。

検索したい任意のロググループをクリックします。

「すべてのログストリームを検索」をクリックします。

ログイベントが表示されました。

検索してみましょう。主な検索構文は以下の通りです。
Using filter patterns to match terms in log events

単語検索

単語が含まれるログイベントを返します。

ERROR

AND 検索

指定した複数の単語が含まれるログイベントを返します。
半角スペースで区切ってください。

ERROR ARGUMENTS

OR 検索

指定した複数の単語のうち何れかが含まれるログイベントを返します。
単語の前に疑問符 ("?") を置きます。

?ERROR ?ARGUMENTS

完全一致検索

一致した正確なフレーズが含まれるログイベントを返します。
ダブルクォーテーションで囲います。

"INTERNAL SERVER ERROR"

除外する

指定された単語を除外したログイベントを返します。
除外する単語の前にマイナス記号を置きます。

ERROR -ARGUMENTS

文字列に一致する JSON ログ

指定したプロパティから指定した単語を含むログイベントを返します。
JSON ログを検索する際には、中かっこで囲ってください。

{ $.eventType = "UpdateTrail" }

数値に一致する JSON ログ

指定したプロパティから指定した数値に一致したログイベントを返します。
より大きい (">")、より小さい ("<")、等しい ("=")、等しくない ("!=")、より大きいまたは等しい (") >=")、または以下 ("<=")。

{ $.bandwidth > 75 }

JSON ログの AND 検索

&& 前後の式に一致したログイベントを返します。

{ ($.user.id = 1) && ($.users[0].email = "John.Doe@example.com") }

JSON ログの OR 検索

|| 前後の式の何れかに一致するログイベントを返します。

{ $.user.email = "John.Stiles@example.com" || $.coordinates[0][1] = "nonmatch" }

スペース区切りの非構造化ログ検索

非構造化ログがスペースで区切られている場合、区切られているフィールドごとに単語で検索することが可能です。
重要度 = err で検索したい、しかし、重要度以外の文字列に err が含まれてしまうということは、よくあることだと思います。

[ip, user, username, timestamp, request =*.html*, status_code = 4*, bytes]

検索日時の指定

ログは事象発生時間帯に絞って検索したくなる場面が多いと思います。
CloudWatch Logs も時間帯を限定することが可能です。
相対指定、絶対指定から使いやすい指定方法をご利用ください。

検索その2 AWS CLI

AWS CLI でログイベントを検索します。
従来から tail, grep, awk などでログ検索していたエンジニアの皆様には親しみやすい方法だと思います。
AWS CLI v1 でも検索は可能ですが、AWS CLI v2 のほうがより便利です。

お手元の PC からでも CloudShell からでもお好きなターミナルで検索しましょう!
AWS CloudShellをマネジメントコンソールと同じ画面で操作できるようになりました!

基本的なコマンドは以下です。

aws logs filter-log-events --log-group-name ロググループ名

ただ、上記ですと大量のログを検索してしまうことになるので、時間帯や文字列で検索範囲を絞ります。
--start-time--end-time で時間帯を指定、--filter-patter は前の「ログイベント検索」で解説したパターンを入力します。

aws logs filter-log-events \
  --log-group-name ロググループ名 \
  --start-time `TZ=Asia/Tokyo date --date='2023-04-19 00:00:00.000' +%s%3N` \
  --end-time  `TZ=Asia/Tokyo date --date='2023-04-19 23:59:59.999' +%s%3N` \
  --filter-pattern フィルターパターン

検索結果はデフォルト JSON で出力されるので jq を使ってバリバリに検索しましょう。
JSON が不便だと感じた場合は TEXT 形式に変換することも可能です。

aws logs filter-log-events \
  --log-group-name ロググループ名 \
  --start-time `TZ=Asia/Tokyo date --date='2023-04-19 00:00:00.000' +%s%3N` \
  --end-time  `TZ=Asia/Tokyo date --date='2023-04-19 23:59:59.999' +%s%3N` \
  --filter-pattern フィルターパターン \
  --output text

AWS CLI v2 では tail が実装されています。tail コマンドと同様の操作をしたい方におすすめです。
--since 現時刻よりどの程度遡って表示するのかを指定します。指定しないと 10分 になります。s = seconds、m = minutes、h = hours のような指定になります。
--follow 新しいログが継続的に流れます。「tail -f」と同じです。

aws logs tail ロググループ名 --since 1m --follow  --filter-pattern フィルターパターン

filter-log-events
tail

検索その3 CloudWatch Logs Insights

ログをインタラクティブに検索・分析したい場合は CloudWatch Logs Insights を使います。

CloudWatch Logs Insights でログを表示すると、検索に必要なフィールドを自動生成してくれます。このフィールドと専用のクエリ構文を駆使してインタラクティブなログ検索を実現します。

フィールド

自動生成してくれるフィールドは以下の通りです。

  • @message = 生ログデータ
  • @timestamp = タイムスタンプ
  • @ingestionTime = CloudWatch がログを受信した時刻
  • @logStream = ログストリーム名
  • @log = ロググループ識別子

クエリ構文

fields

クエリ結果に表示するフィールドを指定します。
以下の例ですと、timestamp と message が結果に表示されます。

fields @timestamp, @message
| sort @timestamp desc
| limit 20

display

クエリ結果に表示するフィールドを指定します。フィールドそのものを表示するというより、フィールドを加工して検索した結果を表示するといった用途に向いているように思います。
display は1回だけ使用できます。

以下の例ですと、「[ERROR] any error messages」というログイベントのうち「any error messages」がクエリ結果に表示されます。

fields @message
| parse @message "[*] *" as loggingType, loggingMessage
| filter loggingType = "ERROR"
| display loggingMessage

filter

条件に合致するログイベントを抽出するために使用します。

fields @timestamp, @message, @logStream, @log
| filter @message like "DEBUG"
| sort @timestamp desc
| limit 20

他すべての構文は書ききれないくらい用意されています。以下を参照ください。
CloudWatch Logs Insights クエリ構文

クエリ保存と再実行

よく使うクエリは保存しましょう。保存したクエリは再実行可能です。

保存

クエリを記述する領域のすぐ下に保存ボタンがあります。
既存クエリを少し変更して別名で保存したい場合は、アクション → 名前を付けて保存 をクリックします。

再実行

CloudWatch Logs Insights 画面の右側にクエリボタンがあります。ここをクリックすると保存されたクエリが表示されます。 再実行したい任意のクエリをクリックすると、

クロスアカウント

複数アカウントの CloudWatch Logs を面倒な切り替え無しで表示・検索したいと長い間感じていましたが、 アメリカ時間の2022年11月27日に機能が追加され、実現可能になりました。

手順などは以下のブログを参照ください。
Amazon CloudWatchが複数のAWSアカウントにわたるクロスアカウントのオブザーバビリティを開始しました

まとめ

そもそもとしてログは検索しにくいものです。オンプレの頃からそう感じていました。
CloudWatch Logs は見にくいからファイルで欲しい、という要望は稀にあります。が、ログファイルがあったところで grep 祭りをするくらいなら CloudWatch Logs のほうが少しだけ便利だと思います。
障害解析やアクセス分析には Insights が役立ちます。CloudWatch Logs を改めて見直してみると新しい発見があるかもしれません。

参考

AWS CLIを使用してCloudWatchLogsを操作してみる

以上、吉井 亮 がお届けしました。