見出し画像

バッチ処理のログデータを分析するためにCloudWatch Logs Insightsを使ってみた

こんにちは、スペースマーケットのエンジニアの鈴木です。
最近は、プロジェクターを使って大画面で映画や海外ドラマを観ることにハマっています。

スペースマーケットにも大画面で映画鑑賞やゲームができる素敵なスペースがたくさんありますね。

↓こちらは私が「使ってみたい!」と感じたスペースリストです。


スペースマーケットではこのように、お気に入りのスペースをグループ化して比較検討したり共有できたりする機能がありますので、仲間内で場所探しをする際にぜひ利用してみてください。

前置きが少し長くなりましたが、今回はタイトルの通り、CloudWatch Logs Insightsを用いたログデータの分析方法をご紹介します。

やりたかったこと

大量のメール配信機能の本番リリースを行う前に、「正しいユーザーを対象に、正しいコンテンツが送信されるどうか」をテストする必要がありました。

そこで、実際のメール送信は行わず、「どのユーザーに対してどのコンテンツを配信するか」といったログのみを出力するという総合テストを行うことにしました。

前提として、当該のメール配信機能は、AWSのバッチ処理のマネージドサービスであるAWS Batchを用いてECS上で実行され、そのログデータはCloudWach Logs上に保管されるようにあらかじめ設定済みとします。

上記の処理で貯めたログデータに対し、CloudWatch Logs Insightという機能を使って、ログの抽出、分析を行うこととしました。

CloudWatch Logs Insightsとは

以下、公式(CloudWatch Logs Insights を使用したログデータの分析)からの引用です。

CloudWatch Logs Insights では、Amazon CloudWatch Logs のログデータをインタラクティブに検索して分析できます。クエリを実行することで、運用上の問題に効率的かつ効果的に対応できます。問題が発生した場合は、CloudWatch Logs Insights を使用して潜在的原因を特定し、デプロイした修正を検証できます。

CloudWatch Logs Insights には専用のクエリ言語といくつかのシンプルで強力なコマンドが含まれています。また、サンプルクエリ、コマンドの説明、クエリの自動補完、ログフィールドの検出を使用して CloudWatch Logs Insights を開始できます。サンプルクエリは、AWS のサービスの複数のログタイプ向けに用意されています。

クエリの基本的な構文の使い方は以下のリンクで詳しく解説されています。

7つのクエリコマンドがサポートされています。概要は以下の通りです。

display: クエリ結果に表示するフィールドを指定する
fields: 指定したフィールドをログイベントから取得して表示する
filter: クエリの結果を 1 つ以上の条件に基づいてフィルタリングする
stats: ログフィールドの値に基づいて集約統計を計算する
sort: 取得したログイベントをソートする
limit: クエリから返されるログイベントの数を指定する
parse: ログフィールドからデータを抽出し、1 つ以上の一時的なフィールドを作成してクエリでさらに処理できるようにする

実際にやったこと

まず、アプリケーション側の実装で以下のようなjson形式のログを出力するようにしました。(わかりやすくするために実際のログよりも簡易化しています)

{"user_id":12345,"attachments":[100,200,300]}

上記のようなログに対して、以下のクエリを実行すると次のような結果が得られます。​

filter @logStream like /your-logstream-name/
| fields @timestamp
 | filter ispresent(attachments.0)
 | parse @message /"attachments":\[(?<@attachment_id>[0-9,\s]+)\]/

スクリーンショット 2020-12-21 20.09.29

このクエリを利用したRubyのスクリプトを作成して、「送信時刻時点で送信対象が正しい状態だったかどうか」の判定を行いました。

まずは以下のスクリプトでAWS CLI による対象期間を指定したクエリをまとめて発行し、レスポンスからquery_idを取得してCSVファイルに書き込みます(簡易化のために1レコードのみのCSVファイルになっていますが、実際は対象の期間をずらしながら大量のクエリを実行させていました)

CSV.open("log/job_list.txt", "wb") do |csv|
   started_at = Time.new(2020, 9, 1)
   ended_at = started_at + 1.day
   cmd = %Q{
   aws logs start-query \
     --log-group-name '/aws/batch/job' \
     --start-time #{start_at} \
     --end-time #{end_at} \
     --query-string 'filter @logStream like /your-logstream-name/
       | fields @timestamp, @aid
         | filter ispresent(attachments.0)
         | parse @message /"attachments":\\[(?<@aid>[0-9,\s]+)\\]/'
     }
   res = `#{cmd}`
   
   query_id = JSON.parse(res)['queryId']
   csv << [start_at, ended_at, query_id]
 end

続いて、同じくAWS CLIでquery_idを指定してその結果を取得し、後続の処理でそのデータの検証を行いました。

CSV.open("log/job_list.txt.csv", "wb") do |csv|
   csv << ['timestamp', 'attachment_ids', 'check']
   CSV.foreach("log/job_list.txt", encoding: "UTF-8") do |row| 
     res = `aws logs get-query-results --query-id '#{row[2]}'`
     json = JSON.parse(res)
     
     if json['status'] != "Complete"
       puts "#{row[2]}: #{json['status']}"
     else
       json['results'].each do |sub_results|
         arr = []
         sub_results.each do |result|
           # この中でresultの検証を行い、検証結果をarrに代入する
           .
           ..
           ...
         end
         csv << arr
       end
     end
   end
 end

最後に

今回はCloudWatch Logs Insights を用いたログデータの検証方法について、実際の業務で発生した課題を例に取り上げました。

今回の事例では、利用できる7つのクエリのうち、fields, parse, filterの3つしか使用していないので、ほかのクエリについても利用してみたいと思います。

スペースマーケットでは、アプリケーション開発だけでなくインフラ技術で業務上の課題を解決するのが大好きなバックエンドエンジニアを積極募集中ですので興味がありましたら募集要項を御覧ください!




この記事が気に入ったらサポートをしてみませんか?