

# OpenTelemetry 0.7.0 形式の CloudWatch メトリクスストリームの出力
<a name="CloudWatch-metric-streams-formats-opentelemetry"></a>

OpenTelemetry は、ツール、API、および SDK を集めたものです。これを使用して、分析用のテレメトリデータ (メトリクス、ログ、トレース) を計測し、生成し、収集し、エクスポートできます。OpenTelemetry は Cloud Native Computing Foundation の一部です。詳細については、「[OpenTelemetry](https://opentelemetry.io/)」を参照してください。

OpenTelemetry 0.7.0 の完全な仕様については、「[v0.7.0 release](https://github.com/open-telemetry/opentelemetry-proto/releases/tag/v0.7.0)」を参照してください。

Kinesis レコードには、1 つ以上の `ExportMetricsServiceRequest` OpenTelemetry データ構造を含めることができます。各データ構造は、レコード長をバイト単位で示す `UnsignedVarInt32` のヘッダーで始まります。各 `ExportMetricsServiceRequest` には、一度に複数のメトリクスのデータを含めることができます。

`ExportMetricsServiceRequest` OpenTelemetry データ構造のメッセージの文字列表現を次に示します。OpenTelemetry では、Google Protocol Buffers バイナリプロトコルを使用してシリアル化しますが、これは人には読み取り不可能です。

```
resource_metrics {
  resource {
    attributes {
      key: "cloud.provider"
      value {
        string_value: "aws"
      }
    }
    attributes {
      key: "cloud.account.id"
      value {
        string_value: "2345678901"
      }
    }
    attributes {
      key: "cloud.region"
      value {
        string_value: "us-east-1"
      }
    }
    attributes {
      key: "aws.exporter.arn"
      value {
        string_value: "arn:aws:cloudwatch:us-east-1:123456789012:metric-stream/MyMetricStream"
      }
    }
  }
  instrumentation_library_metrics {
    metrics {
      name: "amazonaws.com/AWS/DynamoDB/ConsumedReadCapacityUnits"
      unit: "1"
      double_summary {
        data_points {
          labels {
            key: "Namespace"
            value: "AWS/DynamoDB"
          }
          labels {
            key: "MetricName"
            value: "ConsumedReadCapacityUnits"
          }
          labels {
            key: "TableName"
            value: "MyTable"
          }
          start_time_unix_nano: 1604948400000000000
          time_unix_nano: 1604948460000000000
          count: 1
          sum: 1.0
          quantile_values {
            quantile: 0.0
            value: 1.0
          }
          quantile_values {
            quantile: 0.95
            value: 1.0
          }          
          quantile_values {
            quantile: 0.99
            value: 1.0
          }
          quantile_values {
            quantile: 1.0
            value: 1.0
          }
        }
        data_points {
          labels {
            key: "Namespace"
            value: "AWS/DynamoDB"
          }
          labels {
            key: "MetricName"
            value: "ConsumedReadCapacityUnits"
          }
          labels {
            key: "TableName"
            value: "MyTable"
          }
          start_time_unix_nano: 1604948460000000000
          time_unix_nano: 1604948520000000000
          count: 2
          sum: 5.0
          quantile_values {
            quantile: 0.0
            value: 2.0
          }
          quantile_values {
            quantile: 1.0
            value: 3.0
          }
        }
      }
    }
  }
}
```

**OpenTelemetry メトリクスデータをシリアル化するトップレベルオブジェクト**

`ExportMetricsServiceRequest` は、OpenTelemetry エクスポーターペイロードをシリアル化するトップレベルのラッパーです。これには、`ResourceMetrics` が 1 つ以上含まれています 。

```
message ExportMetricsServiceRequest {
  // An array of ResourceMetrics.
  // For data coming from a single resource this array will typically contain one
  // element. Intermediary nodes (such as OpenTelemetry Collector) that receive
  // data from multiple origins typically batch the data before forwarding further and
  // in that case this array will contain multiple elements.
  repeated opentelemetry.proto.metrics.v1.ResourceMetrics resource_metrics = 1;
}
```

`ResourceMetrics` は、MetricData オブジェクトを表すトップレベルオブジェクトです。

```
// A collection of InstrumentationLibraryMetrics from a Resource.
message ResourceMetrics {
  // The resource for the metrics in this message.
  // If this field is not set then no resource info is known.
  opentelemetry.proto.resource.v1.Resource resource = 1;
  
  // A list of metrics that originate from a resource.
  repeated InstrumentationLibraryMetrics instrumentation_library_metrics = 2;
}
```

**Resource オブジェクト**

`Resource` オブジェクトは、値のペアを持つオブジェクトで、メトリクスを生成したリソースに関する情報が含まれます。AWS で作成されたメトリクスの場合、データ構造には、EC2 インスタンスや S3 バケットなど、メトリクスに関連するリソースの Amazon リソースネーム (ARN) が含まれます。

`Resource` オブジェクトには、`attributes` という属性が含まれています。これには、キーと値のペアが格納されています。
+ `cloud.account.id` にはアカウント ID が含まれています
+ `cloud.region` にはリージョンが含まれています
+ `aws.exporter.arn` にはメトリクスストリーム ARN が含まれています
+ 「`cloud.provider`」 は常に 「`aws`」 です。

```
// Resource information.
message Resource {
  // Set of labels that describe the resource.
  repeated opentelemetry.proto.common.v1.KeyValue attributes = 1;
  
  // dropped_attributes_count is the number of dropped attributes. If the value is 0,
  // no attributes were dropped.
  uint32 dropped_attributes_count = 2;
}
```

**InstrumentationLibraryMetrics オブジェクト**

instrumentation\$1library フィールドには入力されません。エクスポートするメトリクスフィールドのみに入力されます。

```
// A collection of Metrics produced by an InstrumentationLibrary.
message InstrumentationLibraryMetrics {
  // The instrumentation library information for the metrics in this message.
  // If this field is not set then no library info is known.
  opentelemetry.proto.common.v1.InstrumentationLibrary instrumentation_library = 1;
  // A list of metrics that originate from an instrumentation library.
  repeated Metric metrics = 2;
}
```

**Metric オブジェクト**

Metric オブジェクトには、`DoubleSummaryDataPoint` のリストを含む `DoubleSummary` データフィールドが含まれています。

```
message Metric {
  // name of the metric, including its DNS name prefix. It must be unique.
  string name = 1;

  // description of the metric, which can be used in documentation.
  string description = 2;

  // unit in which the metric value is reported. Follows the format
  // described by http://unitsofmeasure.org/ucum.html.
  string unit = 3;

  oneof data {
    IntGauge int_gauge = 4;
    DoubleGauge double_gauge = 5;
    IntSum int_sum = 6;
    DoubleSum double_sum = 7;
    IntHistogram int_histogram = 8;
    DoubleHistogram double_histogram = 9;
    DoubleSummary double_summary = 11;
  }
}

message DoubleSummary {
  repeated DoubleSummaryDataPoint data_points = 1;
}
```

**MetricDescriptor オブジェクト**

MetricDescriptor オブジェクトには、メタデータが含まれています。詳細については、GitHub の「[metrics.proto](https://github.com/open-telemetry/opentelemetry-proto/blob/main/opentelemetry/proto/metrics/v1/metrics.proto#L110)」を参照してください。

メトリクスストリームの場合、MetricDescriptor には次の内容があります。
+ `name` は `amazonaws.com/metric_namespace/metric_name` になります。
+ `description` は空白になります。
+ `unit` には、メトリクスデータの単位をユニファイドコードの形式 (大文字と小文字が区別される) の計量単位に変換したものが入力されます。詳細については、「[CloudWatch での OpenTelemetry 0.7.0 形式への変換](CloudWatch-metric-streams-formats-opentelemetry-translation.md)」および「[The Unified Code For Units of Measure](https://ucum.org/ucum.html)」を参照してください。
+ `type` は `SUMMARY` になります。

**DoubleSummaryDataPoint オブジェクト**

DoubleSumMaryDataPointオブジェクトには、DoubleSumMaryメトリクスの時系列内の 1 つのデータポイントの値が含まれます。

```
// DoubleSummaryDataPoint is a single data point in a timeseries that describes the
// time-varying values of a Summary metric.
message DoubleSummaryDataPoint {
  // The set of labels that uniquely identify this timeseries.
  repeated opentelemetry.proto.common.v1.StringKeyValue labels = 1;

  // start_time_unix_nano is the last time when the aggregation value was reset
  // to "zero". For some metric types this is ignored, see data types for more
  // details.
  //
  // The aggregation value is over the time interval (start_time_unix_nano,
  // time_unix_nano].
  //
  // Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January
  // 1970.
  //
  // Value of 0 indicates that the timestamp is unspecified. In that case the
  // timestamp may be decided by the backend.
  fixed64 start_time_unix_nano = 2;

  // time_unix_nano is the moment when this aggregation value was reported.
  //
  // Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January
  // 1970.
  fixed64 time_unix_nano = 3;

  // count is the number of values in the population. Must be non-negative.
  fixed64 count = 4;

  // sum of the values in the population. If count is zero then this field
  // must be zero.
  double sum = 5;

  // Represents the value at a given quantile of a distribution.
  //
  // To record Min and Max values following conventions are used:
  // - The 1.0 quantile is equivalent to the maximum value observed.
  // - The 0.0 quantile is equivalent to the minimum value observed.
  message ValueAtQuantile {
    // The quantile of a distribution. Must be in the interval
    // [0.0, 1.0].
    double quantile = 1;

    // The value at the given quantile of a distribution.
    double value = 2;
  }

  // (Optional) list of values at different quantiles of the distribution calculated
  // from the current snapshot. The quantiles must be strictly increasing.
  repeated ValueAtQuantile quantile_values = 6;
}
```

詳細については、「[CloudWatch での OpenTelemetry 0.7.0 形式への変換](CloudWatch-metric-streams-formats-opentelemetry-translation.md)」を参照してください。

# CloudWatch での OpenTelemetry 0.7.0 形式への変換
<a name="CloudWatch-metric-streams-formats-opentelemetry-translation"></a>

CloudWatch では、CloudWatch データを OpenTelemetry 形式に配置するために、いくつかの変換が実行されます。

**名前空間、メトリクス名、ディメンションの変換**

これらの属性は、変換によってエンコードされたキーと値のペアになります。
+ メトリクスの名前空間が含まれるペアが 1 つあります
+ メトリクスの名前が含まれるペアが 1 つあります
+ CloudWatch は、ディメンションごとに、`metricDatum.Dimensions[i].Name, metricDatum.Dimensions[i].Value` のペアを保存します。

**Average、Sum、SampleCount、Min、および Max の変換**

Summary データポイントを使用すると、CloudWatch で 1 つのデータポイントを使用してこれらの統計情報をすべてエクスポートできます。
+ `startTimeUnixNano` には CloudWatch `startTime` が含まれています
+ `timeUnixNano` には CloudWatch `endTime` が含まれています
+ `sum` には Sum の統計が含まれています
+ `count` には SampleCount の統計が含まれています
+ `quantile_values` には 2 つの `valueAtQuantile.value` オブジェクトが含まれています
  + `valueAtQuantile.quantile = 0.0` ( を含む)`valueAtQuantile.value = Min value`
  + `valueAtQuantile.quantile = 0.99` ( を含む)`valueAtQuantile.value = p99 value`
  + `valueAtQuantile.quantile = 0.999` ( を含む)`valueAtQuantile.value = p99.9 value`
  + `valueAtQuantile.quantile = 1.0` ( を含む)`valueAtQuantile.value = Max value`

メトリクスストリームを消費するリソースは、Average (**Sum/SampleCount**) の統計で計算できます。

**単位の変換**

CloudWatch 単位は、次の表に示すように、計量単位のユニファイドコードの形式 (大文字と小文字を区別する) に変換されます。詳細については、「[The Unified Code For Units of Measure](https://ucum.org/ucum.html)」を参照してください。


| CloudWatch | OpenTelemetry | 
| --- | --- | 
|  秒 |  s | 
|  Second または Seconds |  s | 
|  Microsecond |  us | 
|  Milliseconds |  ms | 
|  バイト |  方法 | 
|  Kilobytes |  kBy | 
|  Megabytes |  MBy | 
|  Gigabytes |  GBy | 
|  Terabytes |  TBy | 
|  Bits |  bit | 
|  Kilobits |  kbit | 
|  Megabits |  MBit | 
|  Gigabits |  GBit | 
|  Terabits |  Tbit | 
|  割合 (%) |  % | 
|  カウント |  \$1Count\$1 | 
|  なし |  1 | 

単位がスラッシュで結合されている場合は、両方の単位に OpenTelemetry 変換を適用して変換されます。たとえば、Bytes/Second は By/s に変換されます。

# OpenTelemetry 0.7.0 メッセージを解析する方法
<a name="CloudWatch-metric-streams-formats-opentelemetry-parse"></a>

このセクションでは、OpenTelemetry 0.7.0 の解析を開始する際に役立つ情報を提供します。

まず、言語固有のバインディングを取得する必要があります。これにより、OpenTelemetry 0.7.0 のメッセージを適宜の言語で解析できます。

**言語固有のバインディングを取得するには**
+ 手順は、使用する言語によって異なります。
  + Java を使用するには、次の Maven 依存関係を Java プロジェクトに追加します。[OpenTelemetry Java >> 0.14.1](https://mvnrepository.com/artifact/io.opentelemetry/opentelemetry-proto/0.14.1)。
  + 他の言語を使用するには、次の手順を実行します。

    1. 「[Generating Your Classes](https://developers.google.com/protocol-buffers/docs/proto3#generating)」のリストをチェックして、言語がサポートされていることを確認します。

    1. 「[Download Protocol Buffers](https://developers.google.com/protocol-buffers/docs/downloads)」の手順に従って、Protobuf コンパイラをインストールします。

    1. 「[v0.7.0 release](https://github.com/open-telemetry/opentelemetry-proto/releases/tag/v0.7.0)」で OpenTelemetry 0.7.0 ProtoBuf 定義をダウンロードします。

    1. ダウンロードした OpenTelemetry 0.7.0 ProtoBuf 定義のルートフォルダにいることを確認します。次に、`src` フォルダを作成し、言語固有のバインディングを生成するコマンドを実行します。詳細は、「[Generating Your Classes](https://developers.google.com/protocol-buffers/docs/proto3#generating)」を参照してください。

       以下は、Javascript バインディングを生成する方法の例です。

       ```
       protoc --proto_path=./ --js_out=import_style=commonjs,binary:src \
       opentelemetry/proto/common/v1/common.proto \
       opentelemetry/proto/resource/v1/resource.proto \
       opentelemetry/proto/metrics/v1/metrics.proto \
       opentelemetry/proto/collector/metrics/v1/metrics_service.proto
       ```

次のセクションでは、前の手順を使用して構築できる言語固有のバインディングの使用例について説明します。

**Java**

```
package com.example;

import io.opentelemetry.proto.collector.metrics.v1.ExportMetricsServiceRequest;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

public class MyOpenTelemetryParser {

    public List<ExportMetricsServiceRequest> parse(InputStream inputStream) throws IOException {
        List<ExportMetricsServiceRequest> result = new ArrayList<>();

        ExportMetricsServiceRequest request;
        /* A Kinesis record can contain multiple `ExportMetricsServiceRequest`
           records, each of them starting with a header with an
           UnsignedVarInt32 indicating the record length in bytes:
            ------ --------------------------- ------ -----------------------
           |UINT32|ExportMetricsServiceRequest|UINT32|pExportMetricsService...
            ------ --------------------------- ------ -----------------------
         */
        while ((request = ExportMetricsServiceRequest.parseDelimitedFrom(inputStream)) != null) {
            // Do whatever we want with the parsed message
            result.add(request);
        }

        return result;
    }
}
```

**Javascript**

この例では、生成されたバインディングを含むルートフォルダが `./` であるとしています。

関数 `parseRecord` の data 引数には、次のいずれかの型を指定できます。
+ `Uint8Array` これが最適です
+ `Buffer` ノードの下では最適です
+ `Array.number` 8 ビット整数

```
const pb = require('google-protobuf')
const pbMetrics =
    require('./opentelemetry/proto/collector/metrics/v1/metrics_service_pb')

function parseRecord(data) {
    const result = []

    // Loop until we've read all the data from the buffer
    while (data.length) {
        /* A Kinesis record can contain multiple `ExportMetricsServiceRequest`
           records, each of them starting with a header with an
           UnsignedVarInt32 indicating the record length in bytes:
            ------ --------------------------- ------ -----------------------
           |UINT32|ExportMetricsServiceRequest|UINT32|ExportMetricsService...
            ------ --------------------------- ------ -----------------------
         */
        const reader = new pb.BinaryReader(data)
        const messageLength = reader.decoder_.readUnsignedVarint32()
        const messageFrom = reader.decoder_.cursor_
        const messageTo = messageFrom + messageLength

        // Extract the current `ExportMetricsServiceRequest` message to parse
        const message = data.subarray(messageFrom, messageTo)

        // Parse the current message using the ProtoBuf library
        const parsed =
            pbMetrics.ExportMetricsServiceRequest.deserializeBinary(message)

        // Do whatever we want with the parsed message
        result.push(parsed.toObject())

        // Shrink the remaining buffer, removing the already parsed data
        data = data.subarray(messageTo)
    }

    return result
}
```

**Python**

`var-int` デリミタは自分で読み取るか、内部メソッド `_VarintBytes(size)` と `_DecodeVarint32(buffer, position)` を使用する必要があります。これらは、バッファ内でのサイズバイトの直後の位置を返します。読み取り側は、メッセージからそのバイトのみを読み取る新しいバッファを構築します。

```
size = my_metric.ByteSize()
f.write(_VarintBytes(size))
f.write(my_metric.SerializeToString())
msg_len, new_pos = _DecodeVarint32(buf, 0)
msg_buf = buf[new_pos:new_pos+msg_len]
request = metrics_service_pb.ExportMetricsServiceRequest()
request.ParseFromString(msg_buf)
```

**Go**

`Buffer.DecodeMessage()` を使用します。

**C\$1**

`CodedInputStream` を使用します。このクラスは、サイズ区切りのメッセージを読み取ることができます。

**C\$1\$1**

`google/protobuf/util/delimited_message_util.h` に記述されている関数は、サイズ区切りのメッセージを読み取ることができます。

**その他の言語**

その他の言語については、「[Download Protocol Buffers](https://developers.google.com/protocol-buffers/docs/downloads)」を参照してください。

パーサーを実装するときは、Kinesis レコードに複数の `ExportMetricsServiceRequest` Protocol Buffers メッセージを含めることができることを考慮してください。各メッセージは、レコードの長さをバイト単位で示す `UnsignedVarInt32` のヘッダーで始まります。