aws cliからCloudWatchのメトリクスの情報を取得するメモ

## 概要

メトリクスの情報をcliから取得したい。
知識0の状態のため、調べながら取得を試した手順をメモとして残す。
ここではLambdaのDurationを一括取得する例とする。

[ソースコード](https://github.com/hibohiboo/aws-cdk-v2/blob/master/bin/awscli/lambda-duration.sh)


## 知っておく必要のある概念
[用語の解説](https://qiita.com/kkino1985/items/b607e469ae251cc46e4c#%E7%94%A8%E8%AA%9E%E3%81%AE%E8%A7%A3%E8%AA%AC)と一緒に記載されている図を参照。（[*公式:ClouodWatchの概念](https://docs.aws.amazon.com/ja_jp/AmazonCloudWatch/latest/monitoring/cloudwatch_concepts.html))

* 集約
  * 大きいデータセットの場合、統計セットという事前集約されたデータセットを利用できる
    * 利用できる集計はrawポイントと異なるものもある（パーセンタイルなど）

## cliの基本記法

### getmetricdata
[GetMetricData または GetMetricStatistics のどちらを CloudWatch メトリクスに使用すればよいですか?](https://aws.amazon.com/jp/premiumsupport/knowledge-center/cloudwatch-getmetricdata-api/) より、基本的に[GetMetricData](https://docs.aws.amazon.com/ja_jp/AmazonCloudWatch/latest/APIReference/API_GetMetricData.html)( [*cli](https://docs.aws.amazon.com/cli/latest/reference/cloudwatch/get-metric-data.html) )を利用すればよい模様。

```
aws cloudwatch get-metric-data
--metric-data-queries <value>
--start-time <value>
--end-time <value>
[--scan-by <value>]
[--label-options <value>]
[--cli-input-json <value>]
[--starting-token <value>]
[--page-size <value>]
[--max-items <value>]
[--generate-cli-skeleton <value>]
```


次に、各パラメータを設定するために、何処を見ればいいか考える。

## namespaceの確認方法

アラートを作成画面から確認可能。[*](https://qiita.com/rhino_10/items/336589aef80c88e1f235)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99876/9ffd6247-ca93-37d1-67bc-e3df8d12092b.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99876/35a7de4e-da2d-529f-2f9e-8597163cba46.png)

もしくは

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99876/943d287d-21a5-6d69-7e5d-4964a9a68f2b.png)


### Dimensionの確認
[CloudWatch コンソールでのメトリクスの表示](https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/monitoring-metrics.html)より、LambdaのDimensionは以下から選択する。（組合せ可能）

* FunctionName
* Resource
* ExecuteVersion
* なし

コンソールのメトリクスだと以下の部分がディメンション。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99876/7ba5f6ec-170e-6a72-ca85-f1e5095254af.png)


### 統計の選択
[CloudWatch 統計定義](https://docs.aws.amazon.com/ja_jp/AmazonCloudWatch/latest/monitoring/Statistics-definitions.html)

## 実行

### 平均

```
aws cloudwatch get-metric-data \
--metric-data-queries $(cat query.json | jq -c) \
--start-time 2022-03-12T04:00:00Z \
--end-time 2022-03-12T10:00:00Z 
```


```json:query.json
{
  "Id": "q1",
  "MetricStat": {
    "Metric": {
      "Namespace": "AWS/Lambda",
      "MetricName": "Duration",
      "Dimensions": [
        {
          "Name": "FunctionName",
          "Value": "aws-calta"
        }
      ]
    },
    "Period": 3600,
    "Stat": "Average"
  }
}
```

```json:result
{
  "MetricDataResults": [
    {
      "Id": "q1",
      "Label": "Duration",
      "Timestamps": ["2022-03-12T06:00:00+00:00"],
      "Values": [ 183479.85 ],
      "StatusCode": "Complete"
    }
  ],
  "Messages": []
}

```

## 複数確認
queryは複数配列でしても可能。

```json:query.json
[
  {
    "Id": "q1",
    "MetricStat": {
      "Metric": {
        "Namespace": "AWS/Lambda",
        "MetricName": "Duration",
        "Dimensions": [
          {
            "Name": "FunctionName",
            "Value": "f1"
          }
        ]
      },
      "Period": 60,
      "Stat": "p90"
    }
  },
  {
    "Id": "q2",
    "MetricStat": {
      "Metric": {
        "Namespace": "AWS/Lambda",
        "MetricName": "Duration",
        "Dimensions": [
          {
            "Name": "FunctionName",
            "Value": "f2"
          }
        ]
      },
      "Period": 60,
      "Stat": "p90"
    }
  }
]
```

```json:result
{
 "MetricDataResults": [
 {
 "Id": "q1",
 "Label": "f1",
 "Timestamps": ["2022-03-12T06:49:00+00:00"],
 "Values": [ 183479.85],
 "StatusCode": "Complete"
 },
 {
 "Id": "q2",
 "Label": "f2",
 "Timestamps": ["2022-03-12T06:45:00+00:00"],
 "Values": [ 22207.87],
 "StatusCode": "Complete"
 }
 ],
 "Messages": []
 }

```

## 結果の成型

TimestampとValuesが別の配列に入っていると確認しづらいので、jqを使って正規化。
MetricDataResultsが複数だと複雑になるので、`Id:q1`のみが返るクエリを使ったものとする。

```
cat ./result/result.log | jq '.MetricDataResults[0] | {"id": .Id, "times": .Timestamps, "values": .Values } as $tmp | range(0;($tmp.times|length))|{"id":$tmp.id,"time":$tmp.times[.], "value": $tmp.values[.]} | [.id, .time,.value] | @csv' -r
```

<details>

```php
.MetricDataResults[0] | # 配列は一つだけとする
{"id": .Id, "times": .Timestamps, "values": .Values } as $tmp | #変数に格納
range(0;($tmp.times|length))| # ループの準備。Timestampの数だけループする
{"id":$tmp.id,"time":$tmp.times[.], "value": $tmp.values[.]} | # .にはrangeで作成した数字が入っている
[.id, .time,.value] | # csvにできるように結果を配列にする
@csv' | #csv形式で結果を表示
-r # 文字列を囲むダブルクォーテーションがエスケープされないようにする
```

</details>


```
"q1","2022-03-12T07:43:00+00:00",194197.06
"q1","2022-03-12T07:28:00+00:00",41223.9
```

## lambdaすべてのメトリクスを取得
lambdaの[list-functions](https://docs.aws.amazon.com/cli/latest/reference/lambda/list-functions.html)で[filter](https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/cli-usage-filter.html)を使ってFunctionNameを取得し、ディメンションへの入力として、メトリクスを吐き出すことを考える。


```bash:get-metrics.sh
#!/bin/bash

# このシェルスクリプトのディレクトリの絶対パスを取得。
bin_dir=$(cd $(dirname $0) && pwd)

# 変数
start_time=2022-03-12T04:00:00Z
end_time=2022-03-12T10:00:00Z 

# 空の結果ファイル作成
file_name=$(echo $(date -u +"%Y%m%d_%H%M%S"))
file_path=$bin_dir/result/$file_name.csv
mkdir -p $bin_dir/result
touch $file_path

# lambda関数一覧の取得
tmp_functions=$(aws lambda list-functions --query 'Functions[*].FunctionName' --no-paginate | jq -r '.[]')

jq_query='.MetricDataResults[0] | {"id": .Id, "times": .Timestamps, "values": .Values } as $tmp | range(0;($tmp.times|length))|{"id":$tmp.id,"time":$tmp.times[.], "value": $tmp.values[.]} | [.id, .time,.value] | @csv'

# lambdaごとにメトリクスを取得
for v in ${tmp_functions}
do 
  # 改行文字・空白の削除
  trimed_v=$(echo $v)
  # -はidに使用できないので変換
  tmp_id=$(echo $trimed_v | sed 's/-/_/g')
  echo "get metrics $trimed_v"
  query=$(cat $bin_dir/query.json | jq -c | sed "s/{{FunctionName}}/$trimed_v/" | sed "s/{{FunctionId}}/$tmp_id/")
  # メトリクスの取得
  tmp_result=`aws cloudwatch get-metric-data \
  --metric-data-queries $query \
  --start-time $start_time \
  --end-time $end_time 
  `
  # 結果の書込み
  echo $tmp_result | jq  -r "$jq_query"  >> $file_path
done
```

```json:query.json
{
  "Id": "q_{{FunctionId}}",
  "MetricStat": {
    "Metric": {
      "Namespace": "AWS/Lambda",
      "MetricName": "Duration",
      "Dimensions": [
        {
          "Name": "FunctionName",
          "Value": "{{FunctionName}}"
        }
      ]
    },
    "Period": 3600,
    "Stat": "p90"
  }
}
```





## 気になること
参考にした記事内で書かれていて気になったこと。
公式ドキュメントに根拠が書かれているか調べてみた。

* cloudwatchでは2週間前までのデータしから取得できない ← ホント？ ← データポイントの期間による
* cloudwatchのログは3か月で消える ← ホント？ デフォルト値とか？ ← データポイントの期間による

[メトリクス](https://docs.aws.amazon.com/ja_jp/AmazonCloudWatch/latest/monitoring/cloudwatch_concepts.html#Metric)より。
> メトリクスは作成されたリージョンにのみ存在します。
メトリクスは削除できませんが、それらに対して新しいデータが発行されない場合、15 か月後に自動的に有効期限切れになります。
15 か月以上経過したデータポイントは、ローリング方式で期限切れになります。
つまり、新しいデータポイントが入ってくるたびに、古い 15 か月以上経過したものが削除されます。

また、ログは以下の制限がある。
[メトリクスの保持](https://docs.aws.amazon.com/ja_jp/AmazonCloudWatch/latest/monitoring/cloudwatch_concepts.html)

> CloudWatch では、次のようにメトリクスデータを保持します。

データポイントの期間|保持期間
--|--
60 秒未満|3 時間
 60 秒 (1 分)|15 日間
 300 秒 (5 分)|63 日間
 3600 秒 (1 時間)|455 日 (15 か月) 


## 参考
[CloudWatchメトリクス AWS CLI サンプル集](https://qiita.com/moomindani/items/aef16aa93db56b071ba3)
[公式 aws cli cloudwatch](https://docs.aws.amazon.com/cli/latest/reference/cloudwatch/index.html#cli-aws-cloudwatch)
[CloudWatchメトリクスを定期的に取得するログ監視システムの構築](https://qiita.com/Kept1994/items/aa76ae065d8d16c557a8)
[AWS CLIでWAFのCloudWatchメトリクスを取得するときにはまったはなし](https://qiita.com/rhino_10/items/336589aef80c88e1f235)
[loudwatchのチュートリアルを読み解く](https://qiita.com/kkino1985/items/b607e469ae251cc46e4c)
[CloudWatchメトリクスのデータポイントをAWS CLIで取得する](https://dev.classmethod.jp/articles/get-data-points-for-cloudwatch-metrics-with-api/)
[sed](https://www.wakuwakubank.com/posts/338-linux-sed/)

---
[metric-math](https://docs.aws.amazon.com/ja_jp/AmazonCloudWatch/latest/monitoring/using-metric-math.html#metric-math-syntax)