AWS Lambdaからカスタムメトリクスを送信

## 環境

`ServerlessFramework 1.24.1`
`Node.js 6.10`

## CloudWacth メトリクスについて

Lambdaのメトリクスは以下のようなものがあります

公式ドキュメント
https://docs.aws.amazon.com/ja_jp/AmazonCloudWatch/latest/monitoring/lam-metricscollected.html

### CloudWatchメトリクス画面のキャプチャ

![cloudwatch01.png](https://qiita-image-store.s3.amazonaws.com/0/266407/da8b4377-2f7c-1ef0-2d08-d2fcb84b6978.png)

![cloudwatch02.png](https://qiita-image-store.s3.amazonaws.com/0/266407/54cedd2d-84da-1fc2-71c3-d4a0aad290fe.png)

### メトリクスのフォーマット

公式ドキュメント
https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/CloudWatch.html#putMetricData-property

````javascript
var params = {
  MetricData: [ /* required */
    {
      MetricName: 'STRING_VALUE', /* required */
      Counts: [
        0.0,
        /* more items */
      ],
      Dimensions: [
        {
          Name: 'STRING_VALUE', /* required */
          Value: 'STRING_VALUE' /* required */
        },
        /* more items */
      ],
      StatisticValues: {
        Maximum: 0.0, /* required */
        Minimum: 0.0, /* required */
        SampleCount: 0.0, /* required */
        Sum: 0.0 /* required */
      },
      StorageResolution: 0,
      Timestamp: new Date || 'Wed Dec 31 1969 16:00:00 GMT-0800 (PST)' || 123456789,
      Unit: Seconds | Microseconds | Milliseconds | Bytes | Kilobytes | Megabytes | Gigabytes | Terabytes | Bits | Kilobits | Megabits | Gigabits | Terabits | Percent | Count | Bytes/Second | Kilobytes/Second | Megabytes/Second | Gigabytes/Second | Terabytes/Second | Bits/Second | Kilobits/Second | Megabits/Second | Gigabits/Second | Terabits/Second | Count/Second | None,
      Value: 0.0,
      Values: [
        0.0,
        /* more items */
      ]
    },
    /* more items */
  ],
  Namespace: 'STRING_VALUE' /* required */
};
````

#### 主要なパラメータを説明

* Namespace
 * メトリックデータの名前空間
 * AWS/~ は予約語になっています：https://docs.aws.amazon.com/ja_jp/AmazonCloudWatch/latest/monitoring/aws-namespaces.html 
 * Lambdaの場合は **AWS/Lambda**
* MetricName
 * メトリックの名前
 * 呼び出し、エラーなど、取得している内容がわかる名前をつけるといいです
* Dimensions / Name, Value
 * メトリクス上でグルーピングされたり、フィルタリングしやすくするのに利用します
 * 今回は Nameに **FunctionName** 、Valueに関数名を利用しています
* Timestamp
 * メトリックデータを受信した時刻
 * 今回は 送信時間
* Unit
 * メトリックの単位
 * 今回は 件数を送るので *Count* を利用
* Value
 * メトリックの値
 * 今回は 件数を送るので **1**
* StorageResolution
 * メトリックが高解像度メトリックの指定
 * 今回は 省略

## 実装

Node.js AWS-SDKを利用して送信しています
送信するのに少し時間がかかるので、タイムアウト時間をデフォルト6秒から少し多めに設定しています

### コード

LambdaからCloudWatchに送信できるように権限を設定

````yml:serverless.yml
# Lambda function's IAM Role
# https://docs.aws.amazon.com/ja_jp/general/latest/gr/aws-arns-and-namespaces.html
iamRoleStatements:
  - Effect: 'Allow'
    Action:
      # Gives permission to cloudWatch
      - cloudwatch:PutMetricData
      Resource:
        - '*'
````

````js:hoge.js
module.exports.handler = (event, context, callback) => {
  Logger.info("start.");

  return Promise.resolve()
    .then(validation(event, context))
    .then(save(event, context))
    .then((metricName) => cloudWatch(event, context, metricName))
    .then((result) => callback(null, result))
    .catch((error) => {
      Logger.error(error);
      callback(error);
    });
};

function cloudWatch(event, context, metricName) {
  Logger.info("cloudWatch.");

  return new Promise((resolve, reject)  => {
    //Metricsの設定
    const metric = {
      MetricData: [
        {
          MetricName: metricName,
          Dimensions: [
            {
              Name: 'FunctionName',
              Value: context.functionName
            }
          ],
          Timestamp: new Date(),
          Unit: 'Count',
          Value: 1,
        }
      ],
      Namespace: 'AWS/Lambda'
    };

    Aws.cloudWatch().putMetricData(metric, (err, data) => {
     if (err) {
        reject("Error: " + err);
      } else {
        Logger.debug("data: " + JSON.stringify(data, null, 2));
        resolve();
      }
    });
  });
}
````

### まとめ

これでCloudWatchメトリクスに任意の情報を送信できるようになり
グラフ化して可視化したり、しきい値を決めてアラート設定して監視したりできるようになります

今回は省きましたが、パラメータ　**StorageResolution** を設定することで通常は60秒（1分）が最小の粒度を1秒まで設定することが可能です
ただしメトリクス費用計算も考慮に入れて設定する必要がありますが、1分でまとめられてしまう細かさだと困る場合は利用するといいと思います