cloudwatch-logs-aggregator で Amazon CloudWatch Logs のログのメトリック化を実践する

こんにちは、Mackerel 開発チームアプリケーションエンジニアの id:susisu です。

この記事では cloudwatch-logs-aggregator を使って、Amazon CloudWatch Logs に出力されたアプリケーションのログを集計して Mackerel にメトリックとして投稿し、監視・可視化を行えるようになるまでの過程を、実際の使用例と共に紹介します。

以下のようなアプリケーションのログからメトリックが生成され、Mackerel に投稿されている状態がゴールです。

{
  "level": "info",
  "msg": "query complete",
  "bytes_scanned": 51828,
  "records_scanned": 246,
  "records_matched": 120,
  ...
}

完成イメージ

cloudwatch-logs-aggregator とは

cloudwatch-logs-aggregator は Mackerel が提供する AWS Lambda で動作する関数で、CloudWatch Logs に出力されたログを集計し、その結果を Mackerel にサービスメトリックとして投稿できます。この Lambda 関数はユーザーの AWS アカウント上で構築され、そのための Terraform モジュールが付属しています。

ログの仕組みを活用することで、手軽にアプリケーションのメトリックの監視・可視化を始めることができます。

構成図

cloudwatch-logs-aggregator の機能の詳細については、以下のドキュメントおよびリポジトリの README をご確認ください。

cloudwatch-logs-aggregator 自身のログを集計する

ここからは cloudwatch-logs-aggregator を実際に使用する方法を紹介していきますが、そのためには集計対象となるログを出力するアプリケーションが必要です。

ここではユーザー独自のアプリケーションに代わり、既に構築済みの cloudwatch-logs-aggregator が存在することとして、そのログを集計対象とするような別の cloudwatch-logs-aggregator を新たに構築していくことにします。

cloudwatch-logs-aggregator は大きく分類して 2 種類のログを出力します。

1 つは単純なエラーログです。例えば CloudWatch Logs Insights へのクエリに失敗した場合は、以下のようなログが出力されます。(ここでは読みやすさのために改行などの整形をしています。)

{
  "level": "error",
  "msg": "failed to query: ...",
  ...
}

もう 1 つは実行情報を出力するログです。例えば CloudWatch Logs Insights へのクエリが成功した場合は、以下のようにスキャンしたログの量などのデータが出力されます。

{
  "level": "info",
  "msg": "query complete",
  "bytes_scanned": 51828,
  "records_scanned": 246,
  "records_matched": 120,
  ...
}

エラーログを集計する

まずは 1 つめのエラーログ ("level": "error" として出力されるログ) について集計していきます。

{
  "level": "error",
  "msg": "failed to query: ...",
  ...
}

ここでは単純に、エラーログの数 = エラー数をメトリックとして Mackerel に投稿することとします。アプリケーションのエラー数がメトリックとして得られれば、監視ルールを設定して機械的・継続的な監視を行なったり、グラフを使って可視化することでエラーの発生頻度や傾向を確認できます。

ログを集計するための cloudwatch-logs-aggregator のセットアップは、以下の手順で行います。

  1. メトリック投稿先の Mackerel のサービスを用意する
  2. Mackerel の API キーを AWS Systems Manager の Parameter Store に保存する
  3. cloudwatch-logs-aggregator の Lambda 関数を作成する
  4. ログを集計するクエリを作成する
  5. Lambda 関数を起動する Amazon EventBridge のルールを作成する

1. メトリック投稿先の Mackerel のサービスを用意する

cloudwatch-logs-aggregator はログを集計した結果をサービスメトリックとして Mackerel に投稿するため、あらかじめ投稿先の Mackerel のサービスを用意しておく必要があります。

メトリックの投稿先とする Mackerel のサービスを、一覧から選択または新規作成してください。

この後の例では my-service という名前のサービスをメトリックの投稿先として使用します。

2. Mackerel の API キーを AWS Systems Manager の Parameter Store に保存する

cloudwatch-logs-aggregator がサービスメトリックの投稿に使用する Mackerel の API キーを用意します。

Mackerel の API キー一覧画面から書き込み (Write) 権限のある API キーを取得または新規作成し、AWS Systems Manager の Parameter Store に保存してください。このときパラメータのタイプは SecureString とし、API キーを暗号化して保存することをおすすめします。

この後の例では、Paramter Store の /mackerel/myApiKey という名前のパラメータに API キーが保存されているものとします。

3. cloudwatch-logs-aggregator の Lambda 関数を作成する

cloudwatch-logs-aggregator の本体となる Lambda 関数を作成します。

作成には cloudwatch-logs-aggregator に付属の Terraform モジュールを使用します。Terraform の設定ファイル main.tf を作成し、以下の内容を記述します。

module "cw_logs_aggregator_lambda" {
  source = "github.com/mackerelio-labs/mackerel-monitoring-modules//cloudwatch-logs-aggregator/lambda?ref=v0.1.2"

  region        = "ap-northeast-1"
  function_name = "cw-logs-aggregator-demo"
  iam_role_name = "cw-logs-aggregator-demo-lambda"
}

以下のコマンドを実行すると、確認の後に cloudwatch-logs-aggregator のLambda 関数および関連する IAM ロール等のリソースが作成されます。

terraform init
terraform apply

これで Lambda 関数の作成が完了しました。

Lambda 関数 cw-logs-aggregator-demo が作成された

4. ログを集計するクエリを作成する

CloudWatch Logs に出力されたエラーログの数を数えるための、CloudWatch Logs Insights のクエリを作成します。

ここでは filter コマンドを使ってエラーログを絞り込み、stats コマンドを使ってそれらの数を数えます。クエリの構文についての詳細は AWS のドキュメントを参照してください。

filter level = "error"
| stats count(*) as `error_count`

このクエリを実行すると、error_count という名前でエラーログの数が集計されます。cloudwatch-logs-aggregator はこの名前と値を元に、Mackerel にメトリックを投稿します。

クエリを cloudwatch-logs-aggregator で実行するよう設定する前に、あらかじめ動作確認をしておきましょう。動作確認は AWS のコンソールから行えます。(該当するログが 1 件もなければ結果は空になります。)

クエリの実行結果の例

無事に動作しているようです。

5. Lambda 関数を起動する Amazon EventBridge のルールを作成する

クエリが完成したので、続いて cloudwatch-logs-aggregator が作成したクエリを使うように、Lambda 関数を起動するためのルールを設定します。

この設定にも付属の Terraform モジュールを使用します。先ほど Lambda 関数を作成したのと同じファイル main.tf に、以下の内容を追記します。

module "cw_logs_aggregator_rule_error_count" {
  source = "github.com/mackerelio-labs/mackerel-monitoring-modules//cloudwatch-logs-aggregator/rule?ref=v0.1.2"

  region    = "ap-northeast-1"
  rule_name = "cw-logs-aggregator-demo-error-count"
  # 上で作成した Lambda 関数の ARN
  function_arn = module.cw_logs_aggregator_lambda.function_arn

  # Mackerel の API キーを保存した Parameter Store のパラメータ名
  api_key_name = "/mackerel/myApiKey"
  # メトリック投稿先の Mackerel のサービス名
  service_name = "my-service"

  # 検索対象とする CloudWatch Logs のロググループ名
  # ここでは別の cloudwatch-logs-aggregator のログを対象とする
  log_group_name = "/aws/lambda/cw-logs-aggregator-target"
  # 上で作成したクエリ
  query = <<EOT
    filter level = "error"
    | stats count(*) as `error_count`
  EOT
  # メトリック名に付与する prefix
  # この場合メトリック名は cloudwatch_logs_aggregator.log.error_count となる
  metric_name_prefix = "cloudwatch_logs_aggregator.log"
  # エラーログが 1 件も存在しなかった時に使用するデフォルト値
  default_metrics = {
    "cloudwatch_logs_aggregator.log.error_count" = 0
  }
  # 実行間隔 (1 分間隔で実行)
  schedule_expression = "rate(1 minute)"
  interval_in_minutes = 1
}

以下のコマンドを実行すると、確認の後に cloudwatch-logs-aggregator の Lambda 関数を起動するための各種リソースが作成されます。

terraform init
terraform apply

動作確認

ここまでで cloudwatch-logs-aggregator のエラーログの数がメトリックとして投稿されるようになったはずですので、Mackerel 上で結果を確認してみましょう。

Mackerel の Web コンソールでメトリック投稿先のサービスを表示し、「サービスメトリック」タブを選択します。

cloudwatch_logs_aggregator.log.error_count のグラフ

無事にメトリックが投稿され、グラフが表示されていることが確認できました。

メトリックに対する監視ルールの設定や、カスタムダッシュボードでのグラフの表示は、一般的なサービスメトリックと同様の方法で行えます。

実行情報のログを集計する

続いて 2 つめの実行情報のログ ("level": "info" として出力されるログ) を集計しますが、ここでは特に CloudWatch Logs Insights へのクエリが完了したときのログ ("msg": "query complete") を対象とします。

{
  "level": "info",
  "msg": "query complete",
  "bytes_scanned": 51828,
  "records_scanned": 246,
  "records_matched": 120,
  ...
}

このような実行情報のログに基づくメトリックは、単純なエラーの有無にとどまらないアプリケーションの動作状況を把握するのに大きく役立ちます。例えば cloudwatch-logs-aggregator の場合であれば、CloudWatch Logs Insights の利用料金がクエリでスキャンされたログのバイト数によって決まるため、これがメトリックとして得られることでコストの監視や見積もりに利用できます。

というわけで、ここではログに出力された bytes_scanned の合計値を集計して Mackerel に投稿してみましょう。

cloudwatch-logs-aggregator のセットアップはエラーログの場合と同様ですが、Mackerel のサービスや API キーおよび Lambda 関数は同じものを再利用できるため、以下では 4 番目の手順以降のみを行います。

  1. メトリック投稿先の Mackerel のサービスを用意する
  2. Mackerel の API キーを AWS Systems Manager の Parameter Store に保存する
  3. cloudwatch-logs-aggregator の Lambda 関数を作成する
  4. ログを集計するクエリを作成する
  5. Lambda 関数を起動する Amazon EventBridge のルールを作成する

4. ログを集計するクエリを作成する

"msg": "query complete" という実行情報のログに含まれる bytes_scanned の合計値を計算するための CloudWatch Logs Insights のクエリを作成します。

このためのクエリは以下の通りです。エラーログの場合と異なるのは filter の条件と、合計値の計算のために count ではなく sum 関数を使用している点です。stats コマンドは既存のフィールドと同名のフィールドの作成を許さないため、合計値を表すフィールドの名前に ~ を付与していますが、これは最終的に Mackerel に投稿されるメトリック名には含まれません。

filter level = "info" and msg = "query complete"
| stats sum(bytes_scanned) as `~bytes_scanned`

このクエリを実行すると、~bytes_scanned という名前で bytes_scanned の合計値が集計されるはずです。先ほどと同様に AWS のコンソールから動作確認もしておきましょう。

クエリの実行結果の例

うまく合計値を計算できているようです。

5. Lambda 関数を起動する Amazon EventBridge のルールを作成する

エラーログの場合と同様に、Lambda 関数を作成したときと同じファイル main.tf に、以下の内容を追記します。

module "cw_logs_aggregator_rule_bytes_scanned" {
  source = "github.com/mackerelio-labs/mackerel-monitoring-modules//cloudwatch-logs-aggregator/rule?ref=v0.1.2"

  region       = "ap-northeast-1"
  rule_name    = "cw-logs-aggregator-demo-bytes-scanned"
  function_arn = module.cw_logs_aggregator_lambda.function_arn

  api_key_name = "/mackerel/myApiKey"
  service_name = "my-service"

  log_group_name     = "/aws/lambda/cw-logs-aggregator-target"
  query              = <<EOT
    filter level = "info" and msg = "query complete"
    | stats sum(bytes_scanned) as `~bytes_scanned`
  EOT
  metric_name_prefix = "cloudwatch_logs_aggregator.query"
  default_metrics = {
    "cloudwatch_logs_aggregator.query.bytes_scanned" = 0
  }
  schedule_expression = "rate(1 minute)"
  interval_in_minutes = 1
}

追記した内容を元に、以下のコマンドを実行して、Lambda 関数を起動するための各種リソースを作成します。

terraform init
terraform apply

動作確認

これで cloudwatch-logs-aggregator がスキャンしたログのバイト数がメトリックとして投稿されるようになりました。エラーログの場合と同じように Mackerel のサービスメトリックの画面から結果を確認してみましょう。

cloudwatch_logs_aggregator.query.bytes_scanned のグラフ

こちらもうまく投稿されていることが確認できました。

まとめ

cloudwatch-logs-aggregator を使って、実際に CloudWatch Logs のログを集計してメトリック化し、Mackerel で監視や可視化を行える状態になるまでを紹介しました。

ここまで見てきたように、cloudwatch-logs-aggregator を使用すると、アプリケーションから適切な形のログを CloudWatch Logs に書き出しておくだけで、大きな手間なくアプリケーションのメトリックの監視を始めることができます。Mackerel を使ったアプリケーション監視のための選択肢の 1 つとして、ぜひ手札に加えていただけたらと思います。

cloudwatch-logs-aggregator で取得したメトリックの活用については、以下の記事も参考にしてください。