AWS SDK for JavaScript v3でGetMetricData APIを使ってCloudWatch metricsからデータポイントを取得する

2022.09.20

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

こんにちは、CX事業本部 IoT事業部の若槻です。

CloudWatch metricsのデータポイントの値が期待通りであるかチェックするE2Eテストのリファクタリングで、AWS SDK for JavaScriptをv2からv3に置き換え、さらにデータポイントの取得方法をGetMetricStatisticsからGetMetricDataに置き換えました。

GetMetricStatisticsGetMetricDataはいずれもCloudWatch metricsのデータポイントを取得できる点では同じですが、データ取得のスケーラビリティの観点でGetMetricDataの利用が推奨されています。

しかし、このGetMetricDataを使おうとしたところ、細かい点でGetMetricStatisticsと仕様が異なっており戸惑った箇所があったので、実装した内容含め書き残しておきます。

やってみた

次のようなCloudWatch metricsからデータポイントを取得してみます。

GetMetricData(v3)とJestで次のようなテストコードの実装を行いました。

test/temperatureMetrics.test.ts

import {
  CloudWatchClient,
  GetMetricDataCommand,
} from '@aws-sdk/client-cloudwatch';

const cloudwatchClient = new CloudWatchClient({ region: 'ap-northeast-1' });

test('テスト', async () => {
  // データポイント取得(GetMetricData)
  const response = await cloudwatchClient.send(
    new GetMetricDataCommand({
      MetricDataQueries: [
        {
          Id: 'demoMetricsQuery',
          MetricStat: {
            Metric: {
              Namespace: 'demo',
              MetricName: 'temperature',
            },
            Period: 60,
            Stat: 'Maximum',
          },
        },
      ],
      StartTime: new Date(Date.now() - 60 * 1000),
      EndTime: new Date(),
    })
  );
  const result = response.MetricDataResults;

  // 期待通りの値が取得できたかテスト
  if (result && result[0].Values) {
    const value = result[0].Values[0];
    expect(value).toBeGreaterThanOrEqual(0);
    expect(value).toBeLessThan(30);
  } else {
    throw new Error('MetricDataResults has no result.');
  }
});

比較として、GetMetricStatistics(v2)の実装サンプルがこちらです。

var params = {
  EndTime: new Date || 'Wed Dec 31 1969 16:00:00 GMT-0800 (PST)' || 123456789, /* required */
  MetricName: 'STRING_VALUE', /* required */
  Namespace: 'STRING_VALUE', /* required */
  Period: 'NUMBER_VALUE', /* required */
  StartTime: new Date || 'Wed Dec 31 1969 16:00:00 GMT-0800 (PST)' || 123456789, /* required */
  Dimensions: [
    {
      Name: 'STRING_VALUE', /* required */
      Value: 'STRING_VALUE' /* required */
    },
    /* more items */
  ],
  ExtendedStatistics: [
    'STRING_VALUE',
    /* more items */
  ],
  Statistics: [
    SampleCount | Average | Sum | Minimum | Maximum,
    /* more items */
  ],
  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
};
cloudwatch.getMetricStatistics(params, function(err, data) {
  if (err) console.log(err, err.stack); // an error occurred
  else     console.log(data);           // successful response
});
  • GetMetricStatistics(v2)では一度のAPI実行で1つのNamespaceに対してしかクエリできませんが、GetMetricData(v3)では複数のNamespaceに対して実行可能です。そのためStartTimeおよびEndTime以外のプロパティはMetricDataQueries内で配列形式でクエリデータとして指定することになります。
  • データを取得する期間を指定するStartTimeおよびEndTimeプロパティは、GetMetricStatistics(v2)ではnew Date || 'Wed Dec 31 1969 16:00:00 GMT-0800 (PST)' || 123456789のいずれかの指定で良かったのですが、GetMetricData(v3)ではDate型のみとなります。タイムスタンプ形式で指定している場合は実装の変更が必要です。

テストコードを実行するとテストが成功しました。

$ npx jest getMetricData.test.ts
 PASS  test/getMetricData.test.ts
  ✓ テスト (124 ms)

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        2.553 s, estimated 3 s
Ran all test suites matching /getMetricData.test.ts/i.

(おまけ)検証環境の構築に使ったCDKコード

CloudWatch metricsにデータポイントを毎分発行するState MachineをCDKでを実装しています。

lib/aws-cdk-app-stack.ts

import {
  aws_events,
  aws_events_targets,
  aws_stepfunctions,
  aws_stepfunctions_tasks,
  Stack,
  StackProps,
} from 'aws-cdk-lib';
import { Construct } from 'constructs';

export class AwsCdkAppStack extends Stack {
  constructor(scope: Construct, id: string, props?: StackProps) {
    super(scope, id, props);

    const putMetricTask = new aws_stepfunctions_tasks.CallAwsService(
      this,
      'putMetricTask',
      {
        service: 'cloudwatch',
        action: 'putMetricData',
        parameters: {
          Namespace: 'demo',
          MetricData: [
            {
              MetricName: 'temperature',
              'Value.$': 'States.MathRandom(0, 30)',
            },
          ],
        },
        iamResources: ['*'],
        iamAction: 'cloudwatch:PutMetricData',
      }
    );

    const stateMachine = new aws_stepfunctions.StateMachine(
      this,
      'myStateMachine',
      {
        stateMachineName: 'myStateMachine',
        definition: putMetricTask,
      }
    );

    new aws_events.Rule(this, 'rule', {
      schedule: aws_events.Schedule.cron({
        minute: '*',
      }),
      targets: [new aws_events_targets.SfnStateMachine(stateMachine)],
    });
  }
}

以前のエントリでも同様の実装をしましたが、その際には未提供だった組み込み関数States.MathRandomを使って、ランダムな数値をLambda関数を使わずに生成しています。

Event Bridge Ruleにより1分毎にメトリクスが発行され続けるため、検証が終わったらCDK Destroyでリソースを削除するようにしましょう。

おわりに

AWS SDK for JavaScript v3でGetMetricData APIを使ってCloudWatch metricsからデータポイントを取得してみました。

v3のドキュメントはv2まではあったパラメーターのサンプルが載っていないのでドキュメントをちゃんと読み込む必要があります。必要な情報は記載されているので落ち着いて読むようにしましょう。

参考

以上