Amazon CloudWatchのDatapointをソートするComparatorを作る

CloudWatchに登録されるデータ

Amazon CloudWatchは、AWSの各種サービスの状態履歴を保存するためのサービスです。インスタンスの死活確認やCPU使用率のチェックをはじめ、独自に定義した情報を登録することも可能です。通常はAWS管理コンソールから履歴を確認するのみですが、APIを用いて履歴データを統計情報として取得することもできます。今回は、このCloudWatch APIを用いてサーバーの統計データを取得したいと思います。

統計データを取得するために指定する項目

統計データを取得するためには以下の項目を指定する必要があります。

  • 開始時間:例)10日前
  • 終了時間:例)2日前
  • メトリクス名:例)CPU使用率
  • ネームスペース:例)AWS/EC2
  • ピリオド:例)1分
  • ディメンジョン:例)インスタンスID
  • タイプ:例)平均値

統計データを取得したらソートされていない

それでは統計データを取得してみましょう。

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.List;

import com.amazonaws.auth.ClasspathPropertiesFileCredentialsProvider;
import com.amazonaws.regions.Region;
import com.amazonaws.regions.Regions;
import com.amazonaws.services.cloudwatch.AmazonCloudWatch;
import com.amazonaws.services.cloudwatch.AmazonCloudWatchClient;
import com.amazonaws.services.cloudwatch.model.Datapoint;
import com.amazonaws.services.cloudwatch.model.Dimension;
import com.amazonaws.services.cloudwatch.model.GetMetricStatisticsRequest;
import com.amazonaws.services.cloudwatch.model.GetMetricStatisticsResult;

public class CWStatistics {

    public static void main(String[] args) throws IOException {

    	AmazonCloudWatch cw = new AmazonCloudWatchClient(new ClasspathPropertiesFileCredentialsProvider());
		Region apNorthEast1 = Region.getRegion(Regions.AP_NORTHEAST_1);
		cw.setRegion(apNorthEast1);

		GetMetricStatisticsRequest getMetricStatisticsRequest = new GetMetricStatisticsRequest();
		Date date = new Date();
		getMetricStatisticsRequest.setStartTime(new Date(date.getTime()-(24*3600*1*1000)));
		getMetricStatisticsRequest.setEndTime(new Date());
		getMetricStatisticsRequest.setMetricName("CPUUtilization");
		getMetricStatisticsRequest.setNamespace("AWS/EC2");
		getMetricStatisticsRequest.setPeriod(3600);
		
		Collection<Dimension> dimensions = new ArrayList<Dimension>();
		Dimension dimension = new Dimension();
		dimension.setName("InstanceId");
		dimension.setValue("i-6a024c68");
		dimensions.add(dimension);
		
		getMetricStatisticsRequest.setDimensions(dimensions);
		
		Collection<String> statistics = new ArrayList<String>();
		statistics.add("Average");
		getMetricStatisticsRequest.setStatistics(statistics);
		
		GetMetricStatisticsResult getMetricStatisticsResult = cw.getMetricStatistics(getMetricStatisticsRequest);
		System.out.println(getMetricStatisticsResult.getLabel());
		List<Datapoint> dataPoints = getMetricStatisticsResult.getDatapoints();
		for(Datapoint point : dataPoints){
			Date timeStamp = point.getTimestamp();
			Double avg = point.getAverage();
			System.out.println(timeStamp.getTime()+" : "+avg);
		}
    }

}

実行結果は以下です。あれ?統計データが時刻でソートされていませんね。これではデータとして扱いづらいです。

CPUUtilization
1377226320000 : 4.657666666666667
1377255120000 : 4.490666666666667
1377215520000 : 4.4990000000000006
1377244320000 : 4.434666666666668
1377240720000 : 4.465166666666666
1377262320000 : 4.500333333333333
1377222720000 : 4.5169999999999995
1377233520000 : 5.096
1377247920000 : 4.467
・・・

オブジェクトをソートするためにComparatorを定義する

Javaでは、クラスの順序を定義することができます。定義の仕方はComparatorインタフェースを実装し、compareメソッドで2つのオブジェクトをどのように比較するか書くだけです。以下に記します。

import java.util.Comparator;

import com.amazonaws.services.cloudwatch.model.Datapoint;


public class DataPointComparator implements Comparator<Datapoint> {

	@Override
	public int compare(Datapoint o1, Datapoint o2) {
		
		return (int) (o1.getTimestamp().getTime() - o2.getTimestamp().getTime());
	}

}

そして、ソートするライブラリを使ってオブジェクトのソートをします。

Collections.sort(dataPoints,new DataPointComparator());

これでOKです。dataPointsのリストの中身が日時でソートされます。そして、以下が実行結果です。

CPUUtilization
1377226320000 : 4.657666666666667
1377255120000 : 4.490666666666667
1377215520000 : 4.4990000000000006
1377244320000 : 4.434666666666668
1377240720000 : 4.465166666666666
1377262320000 : 4.500333333333333
1377222720000 : 4.5169999999999995
1377233520000 : 5.096
1377247920000 : 4.467
・・・

ミリ秒でソートされていますね。これを時系列にチャートへプロットすると。。。CloudWatch管理コンソール"もどき"が出来た!

conpare-000

オブジェクトの比較など

JavaのオブジェクトのスーパークラスにObject型があります。このクラスには、equalsメソッドやtoStringメソッドがありまして、2つのオブジェクトを比較する際の比較方法や文字列表現をオーバーライドして定義することができます。

例えば、CloudWatch の Java SDK内にあるDatapointクラスでは、オブジェクト比較のために以下のようなメソッドが定義されています。

@Override
    public boolean equals(Object obj) {
        if (this == obj) return true;
        if (obj == null) return false;

        if (obj instanceof Datapoint == false) return false;
        Datapoint other = (Datapoint)obj;
        
        if (other.getTimestamp() == null ^ this.getTimestamp() == null) return false;
        if (other.getTimestamp() != null && other.getTimestamp().equals(this.getTimestamp()) == false) return false; 
        if (other.getSampleCount() == null ^ this.getSampleCount() == null) return false;
        if (other.getSampleCount() != null && other.getSampleCount().equals(this.getSampleCount()) == false) return false; 
        if (other.getAverage() == null ^ this.getAverage() == null) return false;
        if (other.getAverage() != null && other.getAverage().equals(this.getAverage()) == false) return false; 
        if (other.getSum() == null ^ this.getSum() == null) return false;
        if (other.getSum() != null && other.getSum().equals(this.getSum()) == false) return false; 
        if (other.getMinimum() == null ^ this.getMinimum() == null) return false;
        if (other.getMinimum() != null && other.getMinimum().equals(this.getMinimum()) == false) return false; 
        if (other.getMaximum() == null ^ this.getMaximum() == null) return false;
        if (other.getMaximum() != null && other.getMaximum().equals(this.getMaximum()) == false) return false; 
        if (other.getUnit() == null ^ this.getUnit() == null) return false;
        if (other.getUnit() != null && other.getUnit().equals(this.getUnit()) == false) return false; 
        return true;
    }

また、toStringメソッドも定義されています。

@Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append("{");
        if (getTimestamp() != null) sb.append("Timestamp: " + getTimestamp() + ",");
        if (getSampleCount() != null) sb.append("SampleCount: " + getSampleCount() + ",");
        if (getAverage() != null) sb.append("Average: " + getAverage() + ",");
        if (getSum() != null) sb.append("Sum: " + getSum() + ",");
        if (getMinimum() != null) sb.append("Minimum: " + getMinimum() + ",");
        if (getMaximum() != null) sb.append("Maximum: " + getMaximum() + ",");
        if (getUnit() != null) sb.append("Unit: " + getUnit() );
        sb.append("}");
        return sb.toString();
    }

まとめ

今回の記事のキッカケは、CloudWatchから取得した統計データのリストを時系列で並べてチャートにすることでした。何か目的があってそれを達成するために色々調べるのですが、むかし学習したことがググって記憶が蘇るのは楽しいですね。