AWS Lambdaの監視をLambdaでおこなう

AWS Lambdaを運用しているとエラーを監視したいという要件が多々出てくると思います。 エラー監視をどうするかというと以下の3つのような選択肢が出てくるかなと思います。

  1. CloudWatch アラームを使用
  2. Mackerelのような監視ツールを使用
  3. 独自の監視ツールを作成

要件

CloudWatch アラームを使用は1番簡単にできる選択肢だと思います。 しかし、CloudWatchのアラームは、エラーを起こした関数がわかる、かつ、監視対象をまとめてセットすることができません。
できるのは、以下の2パターンです。

  1. CloudWatchのメトリクス(Lambda>全ての関数>エラー(Errors)) にアラームをセットし、通知
    どのLambdaにエラーが起きたかがわからない。
  2. CloudWatchのメトリクス(Lambda>関数の名称別>[ファンクション名]>エラー(Errors))にアラームをセットし、通知
    監視対象のLambdaを逐一設定する必要がある。

Lambdaが頻繁に追加したり、削除する要件がなければ、この方法でもよかったのですが、
検証と本番を合わせると30以上のLambdaが存在し、また、今後も増えていく予定があったためにこちらの方法は諦め、
3. 独自の監視ツールを作成を選択しました。

独自の監視ツール

構成

f:id:kinoue3:20180506191654p:plain

エラー監視ようのLambdaを作成し、5分ごとに起動するようにしています。

コード

  1. 特定のprefixのlambdaを取得する
lambda.listFunctions({}, (err, res) => {
    if(err) console.log(err);
    res.Functions
      .map(f => f.FunctionName)
      .filter(f => f.startsWith("staging")
  });

2.CloudWatchからErrorMetrics取得、エラーが起こったものに絞り込み

createParams = name => {
    return {
      Namespace: 'AWS/Lambda',
      StartTime: startTime,
      EndTime:  endTime,
      Period: 300,
      MetricName: 'Errors',
      Dimensions: [
        {
          Name: 'FunctionName',
          Value: name
        }
      ],
      Statistics: ["Sum"],
    }  
  };
const fetchMetrics = (name) => {
    return new Promise((resolve, reject) => {
        const params = createParams(name);
        cloudwatch.getMetricStatistics(params, (err, res) => {
          if(err) {
            reject(err)
          }
          if (!res || !res.Datapoints) return;
          errorMetrics = res.Datapoints.filter(r => r.Sum > 0);
          if (errorMetrics.length > 0) {
            resolve(name)
          } else {
            resolve()
          }
        });
    })  
  };

3.slackに通知

  const notifySlack = (errorFunctionNames) => {
    const url = 'https://slack.com/api/chat.postMessage';
    const data = {
      token: ''token,
      channel: '#channel',
      username: 'username',
      text: `error!!!!${errorFunctionNames}`
    };
    const headers = {
      'Content-Type': 'application/json'
    };
    const options = {
      url,
      method: 'POST',
      headers,
      json: true,
      form: data
    };
    request(options, (error, response, body) => {
      console.log(error)
    })
  };