AWS SDK for PHP を利用してCloudWatchのメトリクスとCloudWatchLogsのログを送信する

AWS SDK for PHP を利用して CloudWatchのメトリクスとCloudWatchLogにログを送信した時のメモです。


## 環境

- PHP 5.6
- AWS SDK for PHP version 3.77

## AIMの権限

[Amazon CloudWatch の権限リファレンス](https://docs.aws.amazon.com/ja_jp/AmazonCloudWatch/latest/monitoring/permissions-reference-cw.html) を参照して必要な権限を付与します。

今回は強めの権限がついていたので省略したのですが、これらが必要だと思います。

- メトリクス
  - cloudwatch:PutMetricData
- ログ
  -  logs:DescribeLogStreams
  -  logs:PutLogEvents
- ログ(必要に応じて)
  - logs:CreateLogGroup
  - logs:CreateLogStream
  - logs:DescribeLogGroups

ログは[https://docs.aws.amazon.com/ja_jp/AmazonCloudWatch/latest/logs/QuickStartEC2Instance.html#cloudwatchlogs_iam_role_procedure](https://docs.aws.amazon.com/ja_jp/AmazonCloudWatch/latest/logs/QuickStartEC2Instance.html#cloudwatchlogs_iam_role_procedure) を参考にしてざっくり設定するのが楽チンでした。


## 実装

メトリクスは [CloudWatchClient::PutMetricData(array)](https://docs.aws.amazon.com/aws-sdk-php/v3/api/api-monitoring-2010-08-01.html#putmetricdata) で、ログは [CloudWatchLogs:: putLogEvents(array)](https://docs.aws.amazon.com/aws-sdk-php/v3/api/api-logs-2014-03-28.html#putlogevents) 送信します。

ただ、ぞれぞれに考えなければならないことがあります。

### メトリクスのディメンジョン is 何

`PutMetricData` のParameter Syntax にある　`Dimensions` が謎パラメータでした。

```
$result = $client->putMetricData([
    'MetricData' => [ // REQUIRED
        [
            'Counts' => [<float>, ...],
            'Dimensions' => [
                [
                    'Name' => '<string>', // REQUIRED
                    'Value' => '<string>', // REQUIRED
                ],
                // ...
            ],
            'MetricName' => '<string>', // REQUIRED
            'StatisticValues' => [
                'Maximum' => <float>, // REQUIRED
                'Minimum' => <float>, // REQUIRED
                'SampleCount' => <float>, // REQUIRED
                'Sum' => <float>, // REQUIRED
            ],
            'StorageResolution' => <integer>,
            'Timestamp' => <integer || string || DateTime>,
            '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' => <float>,
            'Values' => [<float>, ...],
        ],
        // ...
    ],
    'Namespace' => '<string>', // REQUIRED
]);
```

CloudWatch の メトリクスを選択するときに空間の次に選択するやつで、メトリクス名の左側に表示されているものです。

RDSの ディメンジョンから 「DbClusterIdentifier, EngineName」を選択すると、下の様に表示され、赤枠で囲った部分がディメンジョンです。
![ディメンジョン.png](https://qiita-image-store.s3.amazonaws.com/0/145742/d36229e7-7fc8-946b-e75c-0cac04e5d763.png)

この場合であれば `VolumeBytesUsed` の値を渡す時に、`Dimensions` を下の様に指定しています。

```php
'Dimensions' => [
    ['Name' => 'DbClusterIdentifier', 'Value' => rdsのクラスターメ名],
    ['Name' => 'EngineName', 'Value' => 'aurora' ]
]
```

### CloudWatchLogsの putLogEvents のパラメータ sequenceToken

APIリファレンスに書いてある通り、ログストリームが空の時以外は指定しなければなりません。
空ではない場合は、前回の `putLogEvents` の戻り値から `sequenceToken` を得て、次回のパラメターに利用します。


> You must include the sequence token obtained from the response of the previous call. An upload in a newly created log stream does not require a sequence token. You can also get the sequence token using DescribeLogStreams. If you call PutLogEvents twice within a narrow time period using the same value for sequenceToken, both calls may be successful, or one may be rejected.

https://docs.aws.amazon.com/aws-sdk-php/v3/api/api-logs-2014-03-28.html#putlogevents より

処理としては下の様になります。

1. `describeLogStreams` でログストリームの情報を取得。得られた場合は `uploadSequenceToken` を控えておく。
2. `putLogEvens` の `sequenceToken` に 1. で得られた `uploadSequenceToken`を設定する。得られなかった場合は 設定しない。
3. `putLogEvens` が成功したら、戻り値の連想配列から `nextSequenceToken` を取得する。
4. その次の `putLogEvens` の `sequenceToken` には 3. の `nextSequenceToken` を指定する。
5. `putLogEvens` が成功したら、戻り値の連想配列から `nextSequenceToken` を取得する。

あとは、4と5の繰り返し。

これを守らないと `putLogEvents ` で `InvalidSequenceTokenException` が発生します。

```
InvalidSequenceTokenException (client): The given sequenceToken is invalid. The next expected sequenceToken is: 49590143729088555375190966164052711632594785739741365618
```

これは、気づくまで、意味不明なエラーでした。