AWS CDKで既存のロググループにメトリクスフィルタを設定する方法

AWS CDKで既存のロググループにメトリクスフィルタを追加する方法を紹介します。この方法ならAWS CloudFormationやWebコンソールで既に作成済みのロググループを消さずに、AWS CDKでメトリクスフィルタを追加することができます。
2020.04.21

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

はじめに

CX事業本部東京オフィスの佐藤智樹です。

AWS CloudFormationで作成済だったり、Webコンソールで作成して本番稼働しているロググループって消さずにメトリクスフィルタを追加したいですよね。手動で追加するかAWS CloudFormationで追加もできますが、今回はAWS CDKで既存のロググループにメトリクスフィルタを追加する方法を紹介します。

1年前ぐらいの似たような記事は外部にあったのですが、AWS CDKのBraiking Changeがあったのか既に機能が使えなくなっていたので参考までに記事にします。

今回紹介するAWS CDKのaws-logはSTABLEになっているのでこの記事から今後そんなに変わらないはず…

メトリクスフィルタを追加する方法

LogGroup内のfromLogGroupArnを使用して既存のロググループを指定します。メトリクスフィルタを追加など全体の実装は以下のようになります。ついでにメトリクスの追加とSNSへのアラートも記載します。

import * as cdk from '@aws-cdk/core';
import * as logs from '@aws-cdk/aws-logs';
import * as cloudwatch from '@aws-cdk/aws-cloudwatch';
import * as sns from '@aws-cdk/aws-sns';

export class AlarmStack extends cdk.Stack {
  constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);
    
    const stageName: string = this.node.tryGetContext('stageName');
    
    const accountId = cdk.Stack.of(this).account;
    const region = cdk.Stack.of(this).region;
    
    // 対象のログを格納するためのマップ
    const logNames = new Map<string, string>();
    logNames.set(
      `${stageName}-sato-test`,
      // メトリクスを適応したいLogGroupのArnを指定
      `arn:aws:logs:${region}:${accountId}:log-group:/aws/lambda/test-sato-20200421`,
    );
    
    // アラート通知先のトピック
    const topic = new sns.Topic(this, 'Topic', {
      displayName: 'SatoSnSTopic',
      topicName: 'SatoTopic',
    });
    
    // WarnMetricFilterの生成
    for (const [key, value] of logNames) {
      const logGroup = logs.LogGroup.fromLogGroupArn(this, key, value);
      new logs.MetricFilter(this, `WARN-${key}`, {
        // ログ出力されたJSON内のlevelがWARNの場合
        filterPattern: { logPatternString: '{ $.level = "WARN" }' },
        logGroup: logGroup,
        metricName: 'Sato/Lambda/WARN',
        metricNamespace: 'SatoLambda',
      });
      const alarm = new cloudwatch.Alarm(this, `WARN-Alarm-${key}`, {
        metric: new cloudwatch.Metric({
          namespace: 'SatoLambda',
          metricName: 'Sato/Lambda/WARN',
        }),
        threshold: 0,
        evaluationPeriods: 1,
      });
      alarm.addAlarmAction({
        bind() {
          return { alarmActionArn: topic.topicArn };
        },
      });
    }
  }
}

上記でアラートの発行も可能になります。

最後にテスト対象となるLambdaで以下のようなログを出力して動作を確認します。

以下の画像のように問題なくアラートを発砲できることが確認できました。

余談

こちらの記事で同じような処理を行っていたのですが、toCloudFormation()がStackから消えていたため現在は同じようにコードを組んでも動かなくなっています。記事内を読むとSNSなどのコンポーネントも構成が当時と変わっているので一部使えなくなっています。AWS CDKの開発の速さやBraking Changeがあることを改めて実感しました。

上記の記事を読んでいくとAWS CDKで生成されたAWS CloudFormationのテンプレートを後から修正する実装をいれていました。この機能が残っていると作成時は柔軟に修正できて便利ですが、どこで変更を入れているのかコード上で分かり辛いので機能が消されたのかなと思います。完全に憶測ですが。

他の参考資料

先に見つけていたのですぐ直せましたが最初この人と同じようにLogGroupのArn名の最後に":*"を付けてエラーになってました…

Using LogGroup.fromLogGroupArn creates log group name with invalid characters