# OpenTelemetry Protocol仕様

> Source: https://www.ymotongpoo.com/works/otel-specs-ja/otlp/specification/


**ステータス**:

* トレース・メトリクス・ログの各シグナルについては[安定（Stable）](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/document-status.md)です。
* プロファイルシグナルについては[開発中（Development）](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/document-status.md)です。

OpenTelemetry Protocol（OTLP）仕様は、テレメトリー発生源、コレクターなどの中間ノード、テレメトリーバックエンドの間でのテレメトリーデータのエンコーディング・伝送・配送の仕組みを規定します。

OTLPは、OpenTelemetryプロジェクトの範囲内で設計された汎用のテレメトリーデータ配送プロトコルです。

## プロトコルの詳細

OTLPは、テレメトリーデータのエンコーディングと、クライアントとサーバーの間でデータを交換するために使われるプロトコルを定義します。

本仕様書は、OTLPが[gRPC](https://grpc.io/)とHTTPトランスポート上でどのように実装されるかを定義し、ペイロードに使われる[Protocol Buffersスキーマ](https://developers.google.com/protocol-buffers/docs/overview)を規定します。

OTLPはリクエスト・レスポンス形式のプロトコルです。クライアントがリクエストを送信し、サーバーが対応するレスポンスを返します。本ドキュメントでは、`Export`という1つのリクエスト型とレスポンス型を定義します。

すべてのサーバーコンポーネントは、次のトランスポート圧縮オプションをサポートしなければなりません（MUST）。

* 圧縮なし。`none`と表記します。
* Gzip圧縮。`gzip`と表記します。

### OTLP/gRPC

下層のgRPCトランスポートを確立した後、クライアントは[Export*ServiceRequest](https://github.com/open-telemetry/opentelemetry-proto)メッセージ（ログ用の[ExportLogsServiceRequest](https://github.com/open-telemetry/opentelemetry-proto/blob/v1.11.0/opentelemetry/proto/collector/logs/v1/logs_service.proto)、メトリクス用の[ExportMetricsServiceRequest](https://github.com/open-telemetry/opentelemetry-proto/blob/v1.11.0/opentelemetry/proto/collector/metrics/v1/metrics_service.proto)、トレース用の[ExportTraceServiceRequest](https://github.com/open-telemetry/opentelemetry-proto/blob/v1.11.0/opentelemetry/proto/collector/trace/v1/trace_service.proto)、プロファイル用の[ExportProfilesServiceRequest](https://github.com/open-telemetry/opentelemetry-proto/blob/v1.11.0/opentelemetry/proto/collector/profiles/v1development/profiles_service.proto)）を使ったユナリーリクエストでテレメトリーデータの送信を開始します。クライアントはサーバーへリクエストの列を継続的に送信し、それぞれのリクエストに対するレスポンスを受け取ることを期待します。

![リクエストとレスポンス](https://raw.githubusercontent.com/open-telemetry/opentelemetry-proto/v1.11.0/docs/img/otlp-request-response.png)

*注: このプロトコルは、クライアントとサーバーの1つのペアの間の配送信頼性に関するものであり、クライアントとサーバーの間の伝送中にデータが失われないことを保証することを目的としています。多くのテレメトリー収集システムには、最終的な宛先に到達するまでにデータが経由する中間ノードがあります（例: アプリケーション → エージェント → コレクター → バックエンド）。このようなシステムにおけるエンドツーエンドの配送保証は、OTLPの範囲外です。本プロトコルで説明する確認応答は、単一のクライアント・サーバーのペアの間で行われるものであり、複数ホップの配送経路にある中間ノードにまで及びません。*

#### OTLP/gRPCリクエスト

サーバーは、設定ミスや悪意のあるクライアントが過大なリクエストを送信することによる過剰なメモリ使用を軽減するため、リクエストを受信する際に（展開後も含めて）メッセージサイズの上限を強制しなければなりません（MUST）。サーバー実装は通常、受信メッセージサイズの既定の上限として4 MiBを強制します。しかし、既定の上限として64 MiBを使うことが推奨されます（RECOMMENDED）。実装はこの上限を設定可能にすべきです（SHOULD）。上限を超えた場合、gRPCサーバー実装は非リトライ可能なエラーとして`RESOURCE_EXHAUSTED`コードを報告しなければなりません（MUST）。

クライアントは、サーバーへの過負荷を避けるため、（圧縮前も含めて）リクエストメッセージのサイズを制限すべきです（SHOULD）。既定の上限として64 MiBを使うことが推奨されます（RECOMMENDED）。実装はこの上限を設定可能にすべきです（SHOULD）。上限を超えた場合、クライアントはそのリクエストを行ってはならず（MUST NOT）、リクエストが破棄された事実を記録すべきです（SHOULD）。

#### OTLP/gRPC同時リクエスト

リクエストを送信した後、クライアントはサーバーからレスポンスを受け取るまで待機してもよい（MAY）です。その場合、サーバーによって未確認のリクエストは最大でも1つしか存在しません。

![ユナリー](https://raw.githubusercontent.com/open-telemetry/opentelemetry-proto/v1.11.0/docs/img/otlp-sequential.png)

実装の単純さが望まれる場合や、クライアントとサーバーが非常に低レイテンシーなネットワーク（例えばクライアントが計装対象のアプリケーションで、サーバーがローカルデーモン（エージェント）として動作するOpenTelemetry Collectorである場合）で接続されている場合には、逐次実行が推奨されます。

高いスループットを達成する必要がある実装は、より高いスループットを達成するために同時ユナリー呼び出しをサポートすべきです（SHOULD）。クライアントは、先に送信したリクエストへのレスポンスを待たずに新しいリクエストを送信すべきで（SHOULD）、これにより現在未確認のまま飛行中のリクエストのパイプラインが実質的に作られます。

![同時実行](https://raw.githubusercontent.com/open-telemetry/opentelemetry-proto/v1.11.0/docs/img/otlp-concurrent.png)

同時リクエストの数は設定可能であるべきです（SHOULD）。

達成可能な最大スループットは`max_concurrent_requests * max_request_size / (network_latency + server_response_time)`です。例えば、1つのリクエストに最大100個のスパンを含められ、ネットワークの往復レイテンシーが200ms、サーバーの応答時間が300msであれば、同時リクエストが1つの場合の達成可能な最大スループットは`100 spans / (200ms+300ms)`、つまり毎秒200スパンになります。高レイテンシーなネットワークや、サーバーの応答時間が長い場合に良好なスループットを達成するには、リクエストを非常に大きくするか、多数の同時リクエストを行う必要があることがわかります。

クライアントがシャットダウンする場合（例えば、それを含むプロセスが終了しようとする場合）、クライアントはすべての保留中の確認応答を受け取るまで、または実装固有のタイムアウトが経過するまで、任意に待機します。これによりテレメトリーデータの確実な配送が保証されます。クライアント実装は、シャットダウン中の待機をオンまたはオフにするオプションを提供すべきです（SHOULD）。

クライアントが特定のリクエストを配送できない場合（例えば確認応答を待っている間にタイマーが失効した場合）、クライアントはデータが配送されなかった事実を記録すべきです（SHOULD）。

#### OTLP/gRPCレスポンス

レスポンスは適切なメッセージでなければなりません（MUST）（[完全な成功](#完全な成功)、[部分的な成功](#部分的な成功)、[失敗](#失敗)の各ケースで使うべき具体的なメッセージについては以下を参照してください）。

クライアントは、設定ミスや悪意のあるサーバーによる過剰なメモリ使用を軽減するため、レスポンスを受信する際に（展開後も含めて）メッセージサイズの上限を強制しなければなりません（MUST）。gRPCクライアント実装は通常、受信メッセージサイズの既定の上限として4 MiBを強制しますが、これは使用しても構いません。実装はこの上限を設定可能にすべきです（SHOULD）。上限を超えた場合、クライアントはそのレスポンスを非リトライ可能なエラーとして扱わなければなりません（MUST）。なお、このようなシナリオでは、gRPCクライアント実装は呼び出し元に`RESOURCE_EXHAUSTED`コードを報告します。

サーバーは、クライアントへの過負荷を避けるため、（圧縮前も含めて）レスポンスメッセージのサイズを制限しなければなりません（MUST）。既定の上限として4 MiBを使うことが推奨されます（RECOMMENDED）。実装はこの上限を設定可能にすべきです（SHOULD）。上限を超えることになる[部分的な成功](#部分的な成功)のレスポンスについては、可能かつ実用的であれば、レスポンスの意味を変えずにレスポンスサイズを削減すべきです（SHOULD）。そのために、サーバーは`partial_success.error_message`のような任意の診断フィールドの詳細度を下げたり、任意の診断フィールドを省略したりしてもよい（MAY）です。それでもレスポンスが上限に収まらない場合、サーバーは非リトライ可能なエラーとして`RESOURCE_EXHAUSTED`コードでそのリクエストを失敗させなければなりません（MUST）。このような失敗の後、リクエストに含まれていたテレメトリーデータが受理されたかどうかは規定されません。

##### 完全な成功

成功レスポンスは、テレメトリーデータがサーバーに正常に受理されたことを示します。

サーバーが空のリクエスト（テレメトリーデータを一切含まないリクエスト）を受信した場合、サーバーは成功で応答すべきです（SHOULD）。

成功時、サーバーのレスポンスは[Export\<signal>ServiceResponse](https://github.com/open-telemetry/opentelemetry-proto/tree/v1.11.0/opentelemetry/proto/collector)メッセージ（トレース用の`ExportTraceServiceResponse`、メトリクス用の`ExportMetricsServiceResponse`、ログ用の`ExportLogsServiceResponse`、プロファイル用の`ExportProfilesServiceResponse`）でなければなりません（MUST）。

サーバーは、成功レスポンスの場合、`partial_success`フィールドを未設定のままにしなければなりません（MUST）。

##### 部分的な成功

リクエストが部分的にのみ受理された場合（つまりサーバーがデータの一部のみを受理し、残りを拒否した場合）、サーバーのレスポンスは[完全な成功](#完全な成功)のケースと同じ[Export\<signal>ServiceResponse](https://github.com/open-telemetry/opentelemetry-proto/tree/v1.11.0/opentelemetry/proto/collector)メッセージでなければなりません（MUST）。

さらに、サーバーは`partial_success`フィールド（トレース用の`ExportTracePartialSuccess`メッセージ、メトリクス用の`ExportMetricsPartialSuccess`メッセージ、ログ用の`ExportLogsPartialSuccess`メッセージ、プロファイル用の`ExportProfilesPartialSuccess`）を初期化しなければならず（MUST）、拒否したスパン・データポイント・ログレコード・プロファイルの数を、それぞれ対応する`rejected_spans`、`rejected_data_points`、`rejected_log_records`、`rejected_profiles`フィールドに設定しなければなりません（MUST）。

サーバーは、`error_message`フィールドに英語での人間が読めるエラーメッセージを設定すべきです（SHOULD）。このメッセージでは、サーバーがデータの一部を拒否した理由を説明し、問題への対処方法についてのガイダンスを提供してもよいでしょう。本プロトコルはこのエラーメッセージの構造を定義しません。

サーバーは、リクエストを完全に受理した場合であっても、`partial_success`フィールドを使ってクライアントへ警告や提案を伝えてもよい（MAY）です。この場合、`rejected_<signal>`フィールドの値は`0`でなければならず（MUST）、`error_message`フィールドは空でないものでなければなりません（MUST）。

クライアントは、`partial_success`が設定された部分的な成功のレスポンスを受け取った場合、そのリクエストをリトライしてはなりません（MUST NOT）。

##### 失敗

サーバーがエラーを返す場合、それは次の2つの大きなカテゴリーのいずれかに分類されます。リトライ可能なエラーと、リトライ不可能なエラーです。

- リトライ可能なエラーは、テレメトリーデータの処理が失敗したことを示し、クライアントはそのエラーを記録し、同じデータの再エクスポートを試みてもよいでしょう（SHOULD）。例えば、サーバーが一時的にデータを処理できない場合にこれが発生します。

- リトライ不可能なエラーは、テレメトリーデータの処理が失敗したことを示し、クライアントは同じテレメトリーデータの再送信を試みてはなりません（MUST NOT）。クライアントはそのテレメトリーデータを破棄しなければなりません（MUST）。例えば、リクエストに不正なデータが含まれていて、サーバーがそれを逆シリアル化または処理できない場合にこれが発生します。クライアントは、このように破棄したデータのカウンターを保持すべきです（SHOULD）。

サーバーは、リトライ可能なエラーを示すために[Unavailable](https://godoc.org/google.golang.org/grpc/codes)コードを使うべきで（SHOULD）、[RetryInfo](https://github.com/googleapis/googleapis/blob/6a8c7914d1b79bd832b5157a09a9332e8cbd16d4/google/rpc/error_details.proto#L40)を使って[statusを介した追加の詳細](https://godoc.org/google.golang.org/grpc/status#Status.WithDetails)を提供してもよい（MAY）です。以下はこれを示すGoのサンプルコードです。

```go
  // Do this on server side.
  st, err := status.New(codes.Unavailable, "Server is unavailable").
    WithDetails(&errdetails.RetryInfo{RetryDelay: &duration.Duration{Seconds: 5}})
  if err != nil {
    log.Fatal(err)
  }

  return st.Err()
```

リトライ不可能なエラーを示すには、サーバーは[InvalidArgument](https://godoc.org/google.golang.org/grpc/codes)コードを使うことが推奨されており、[BadRequest](https://github.com/googleapis/googleapis/blob/6a8c7914d1b79bd832b5157a09a9332e8cbd16d4/google/rpc/error_details.proto#L119)を使って[statusを介した追加の詳細](https://godoc.org/google.golang.org/grpc/status#Status.WithDetails)を提供してもよい（MAY）です。より適切であれば、別のgRPCステータスコードを使ってもよいでしょう。以下はこれを示すGoのサンプルコードの一部です。

```go
  // Do this on the server side.
  st, err := status.New(codes.InvalidArgument, "Invalid Argument").
    WithDetails(&errdetails.BadRequest{})
  if err != nil {
    log.Fatal(err)
  }

  return st.Err()
```

サーバーは、特定のエラー状況に対してより適切な場合、リトライ可能・リトライ不可能を示すために他のgRPCコードを使ってもよい（MAY）です。クライアントは、次の表に従ってgRPCステータスコードをリトライ可能・リトライ不可能として解釈すべきです（SHOULD）。

|gRPC Code|Retryable?|
|---------|----------|
|CANCELLED|Yes|
|UNKNOWN|No|
|INVALID_ARGUMENT|No|
|DEADLINE_EXCEEDED|Yes|
|NOT_FOUND|No|
|ALREADY_EXISTS|No|
|PERMISSION_DENIED|No|
|UNAUTHENTICATED|No|
|RESOURCE_EXHAUSTED|Only if the server can recover (see below)|
|FAILED_PRECONDITION|No|
|ABORTED|Yes|
|OUT_OF_RANGE|Yes|
|UNIMPLEMENTED|No|
|INTERNAL|No|
|UNAVAILABLE|Yes|
|DATA_LOSS|Yes|

リトライする際、クライアントは指数バックオフ戦略を実装すべきです（SHOULD）。この例外は、後述するスロットリングのケースで、これはリトライ間隔について明示的な指示を提供します。

クライアントは、サーバーがリソース枯渇からの回復が可能であることを示している場合に限り、`RESOURCE_EXHAUSTED`コードをリトライ可能として解釈すべきです（SHOULD）。これは、サーバーが[RetryInfo](https://github.com/googleapis/googleapis/blob/6a8c7914d1b79bd832b5157a09a9332e8cbd16d4/google/rpc/error_details.proto#L40)を含む[statusを返す](https://godoc.org/google.golang.org/grpc/status#Status.WithDetails)ことで示されます。この場合、サーバーとクライアントの挙動は[OTLP/gRPCスロットリング](#otlpgrpcスロットリング)節で説明したとおりになります。そのようなステータスが返されない場合、`RESOURCE_EXHAUSTED`コードはリトライ不可能として扱われるべきです（SHOULD）。

#### OTLP/gRPCスロットリング

OTLPはバックプレッシャーの通知を許可します。

サーバーがクライアントから受信するデータのペースに追いつけない場合、サーバーはその事実をクライアントに通知すべきです（SHOULD）。クライアントはサーバーへの過負荷を避けるため、自身をスロットリングしなければなりません（MUST）。

gRPCトランスポートを使う場合にバックプレッシャーを通知するには、サーバーは[Unavailable](https://godoc.org/google.golang.org/grpc/codes)コードのエラーを返すべきで（SHOULD）、[RetryInfo](https://github.com/googleapis/googleapis/blob/6a8c7914d1b79bd832b5157a09a9332e8cbd16d4/google/rpc/error_details.proto#L40)を使って[statusを介した追加の詳細](https://godoc.org/google.golang.org/grpc/status#Status.WithDetails)を提供してもよい（MAY）です。以下はこれを示すGoのサンプルコードの一部です。

```go
  // Do this on the server side.
  st, err := status.New(codes.Unavailable, "Server is unavailable").
    WithDetails(&errdetails.RetryInfo{RetryDelay: &duration.Duration{Seconds: 30}})
  if err != nil {
    log.Fatal(err)
  }

  return st.Err()

  ...

  // Do this on the client side.
  st := status.Convert(err)
  for _, detail := range st.Details() {
    switch t := detail.(type) {
    case *errdetails.RetryInfo:
      if t.RetryDelay.Seconds > 0 || t.RetryDelay.Nanos > 0 {
        // Wait before retrying.
      }
    }
  }
```

クライアントがこの通知を受け取った場合、[RetryInfo](https://github.com/googleapis/googleapis/blob/6a8c7914d1b79bd832b5157a09a9332e8cbd16d4/google/rpc/error_details.proto#L40)のドキュメントに記載された推奨事項に従うべきです（SHOULD）。

```
// Describes when the clients can retry a failed request. Clients could ignore
// the recommendation here or retry when this information is missing from the error
// responses.
//
// It's always recommended that clients should use exponential backoff when
// retrying.
//
// Clients should wait until `retry_delay` amount of time has passed since
// receiving the error response before retrying.  If retrying requests also
// fail, clients should use an exponential backoff scheme to increase gradually
// the delay between retries based on `retry_delay` until either a maximum
// number of retries has been reached, or a maximum retry delay cap has been
// reached.
```

`retry_delay`の値はサーバーによって決定され、実装依存です。サーバーは、サーバーが回復するための十分な時間を確保できるほど大きく、かつスロットリングされている間にクライアントがデータを破棄してしまうほど大きくない`retry_delay`値を選ぶべきです（SHOULD）。

#### OTLP/gRPCサービスとProtobuf定義

gRPCサービス定義は[こちら](https://github.com/open-telemetry/opentelemetry-proto/tree/v1.11.0/opentelemetry/proto/collector)にあります。

リクエストとレスポンスのProtobuf定義は[こちら](https://github.com/open-telemetry/opentelemetry-proto/tree/v1.11.0/opentelemetry/proto)にあります。

protoのバージョンと[成熟度レベル](https://github.com/open-telemetry/opentelemetry-proto/blob/v1.11.0/README.md#maturity-level)を必ず確認してください。シグナルによってスキーマの成熟度レベルが異なる場合があります。安定しているものもあれば、ベータ版のものもあります。

#### OTLP/gRPCの既定のポート

OTLP/gRPCの既定のネットワークポートは4317です。

### OTLP/HTTP

OTLP/HTTPは、[バイナリ形式](#バイナリprotobufエンコーディング)または[JSON形式](#json-protobufエンコーディング)でエンコードされたProtobufペイロードを使います。エンコーディングに関わらず、メッセージのProtobufスキーマはOTLP/HTTPとOTLP/gRPCで同じであり、[こちら](https://github.com/open-telemetry/opentelemetry-proto/tree/v1.11.0/opentelemetry/proto)で定義されています。

OTLP/HTTPは、クライアントからサーバーへテレメトリーデータを送信するためにHTTP POSTリクエストを使います。実装はHTTP/1.1またはHTTP/2トランスポートを使ってもよい（MAY）です。HTTP/2トランスポートを使う実装は、HTTP/2接続を確立できない場合にHTTP/1.1トランスポートへフォールバックすべきです（SHOULD）。

#### バイナリProtobufエンコーディング

バイナリProtobufでエンコードされたペイロードは、proto3の[エンコーディング標準](https://developers.google.com/protocol-buffers/docs/encoding)を使います。

クライアントとサーバーは、バイナリProtobufでエンコードされたペイロードを送信する際、リクエストとレスポンスのヘッダーに「Content-Type: application/x-protobuf」を設定しなければなりません（MUST）。

#### JSON Protobufエンコーディング

JSON Protobufでエンコードされたペイロードは、ProtobufとJSONの間のマッピングについてproto3標準で定義された[JSONマッピング](https://developers.google.com/protocol-buffers/docs/proto3#json)を使いますが、そのマッピングから次の点で異なります。

- `traceId`と`spanId`のバイト配列は、[大文字小文字を区別しない16進エンコード文字列](https://tools.ietf.org/html/rfc4648#section-8)として表現されます。標準の[Protobuf JSONマッピング](https://developers.google.com/protocol-buffers/docs/proto3#json)で定義されているようなbase64エンコードは使いません。16進エンコーディングは、`Span`・`Link`・`LogRecord`などのすべてのOTLP Protobufメッセージにおける`traceId`と`spanId`フィールドで使われます。例えば、Spanの`traceId`フィールドは次のように表現できます。
  { "traceId": "5B8EFFF798038103D269B633813FC60C", ... }

- enumフィールドの値は整数値としてエンコードされなければなりません（MUST）。標準の[Protobuf JSONマッピング](https://developers.google.com/protocol-buffers/docs/proto3#json)がenumフィールドの値を整数値またはenum名文字列のいずれかでエンコードできることを許しているのとは異なり、OTLP JSON Protobufエンコーディングでは整数のenum値のみが許可され、enum名文字列は使ってはなりません（MUST NOT）。例えば、SpanでSPAN_KIND_SERVERという値を持つ`kind`フィールドは次のように表現できます。{ "kind": 2, ... }

- OTLP/JSONレシーバーは、未知の名前を持つメッセージフィールドを無視しなければならず（MUST）、その未知のフィールドがペイロードに存在しなかった場合と同様にメッセージをアンマーシャルしなければなりません（MUST）。これはバイナリProtobufアンマーシャラーの挙動と整合しており、OTLPメッセージに新しいフィールドを追加しても既存のレシーバーが壊れないことを保証します。

- JSONオブジェクトのキーは、lowerCamelCaseに変換されたフィールド名です。元のフィールド名をJSONオブジェクトのキーとして使うことは有効ではありません。例えば、Resourceの有効なJSON表現は次のとおりです。
  `{ "attributes": {...}, "droppedAttributesCount": 123 }`
  次は有効な表現ではありません。
  `{ "attributes": {...}, "dropped_attributes_count": 123 }`

なお、[Protobuf仕様](https://developers.google.com/protocol-buffers/docs/proto3#json)によれば、JSONエンコードされたペイロードにおける64ビット整数の数値は10進数の文字列としてエンコードされ、デコード時には数値または文字列のいずれも受け付けられます。

クライアントとサーバーは、JSON Protobufでエンコードされたペイロードを送信する際、リクエストとレスポンスのヘッダーに「Content-Type: application/json」を設定しなければなりません（MUST）。

JSONペイロードの例については、[OTLP JSONリクエストの例](https://github.com/open-telemetry/opentelemetry-proto/blob/v1.11.0/examples/README.md)を参照してください。

#### OTLP/HTTPリクエスト

テレメトリーデータはHTTP POSTリクエストで送信されます。POSTリクエストの本文は、バイナリエンコードされたProtobuf形式、またはJSONエンコードされたProtobuf形式のいずれかのペイロードです。

トレースデータを運ぶリクエストの既定のURLパスは`/v1/traces`です（例えば「example.com」サーバーに接続する場合の完全なURLは`https://example.com/v1/traces`になります）。リクエスト本文は、Protobufでエンコードされた`ExportTraceServiceRequest`メッセージです。

メトリクスデータを運ぶリクエストの既定のURLパスは`/v1/metrics`で、リクエスト本文はProtobufでエンコードされた`ExportMetricsServiceRequest`メッセージです。

ログデータを運ぶリクエストの既定のURLパスは`/v1/logs`で、リクエスト本文はProtobufでエンコードされた`ExportLogsServiceRequest`メッセージです。

プロファイリングデータを運ぶリクエストの既定のURLパスは`/v1development/profiles`で、リクエスト本文はProtobufでエンコードされた`ExportProfilesServiceRequest`メッセージです。

クライアントはコンテンツをgzip圧縮してもよい（MAY）です。その場合、「Content-Encoding: gzip」リクエストヘッダーを含めなければなりません（MUST）。クライアントは、gzipエンコードされたレスポンスを受信できる場合、「Accept-Encoding: gzip」リクエストヘッダーを含めてもよい（MAY）です。

リクエストの既定でないURLパスは、クライアントとサーバーの両側で設定できます（MAY）。

サーバーは、設定ミスや悪意のあるクライアントが過大なリクエストを送信することによる過剰なメモリ使用を軽減するため、リクエスト本文を解析する際に（展開後も含めて）サイズの上限を強制しなければなりません（MUST）。既定の上限として64 MiBを使うことが推奨されます（RECOMMENDED）。実装はこの上限を設定可能にすべきです（SHOULD）。上限を超えた場合、サーバーは`HTTP 413 Content Too Large`で応答しなければなりません（MUST）。

クライアントは、サーバーへの過負荷を避けるため、（圧縮前も含めて）リクエスト本文のサイズを制限すべきです（SHOULD）。既定の上限として64 MiBを使うことが推奨されます（RECOMMENDED）。実装はこの上限を設定可能にすべきです（SHOULD）。上限を超えた場合、クライアントはそのリクエストを行ってはならず（MUST NOT）、リクエストが破棄された事実を記録すべきです（SHOULD）。

#### OTLP/HTTPレスポンス

レスポンス本文は適切なシリアライズ済みProtobufメッセージでなければなりません（MUST）（[完全な成功](#完全な成功-1)、[部分的な成功](#部分的な成功-1)、[失敗](#失敗-1)の各ケースで使うべき具体的なメッセージについては以下を参照してください）。

クライアントは、設定ミスや悪意のあるサーバーによる過剰なメモリ使用を軽減するため、レスポンス本文を解析する際に（展開後も含めて）サイズの上限を強制しなければなりません（MUST）。既定の上限として4 MiBを使うことが推奨されます（RECOMMENDED）。実装はこの上限を設定可能にすべきです（SHOULD）。上限を超えた場合、クライアントはそのレスポンスを非リトライ可能なエラーとして扱わなければならず（MUST）、レスポンスが破棄された事実を記録すべきです（SHOULD）。

サーバーは、クライアントへの過負荷を避けるため、（圧縮前も含めて）レスポンス本文のサイズを制限しなければなりません（MUST）。既定の上限として4 MiBを使うことが推奨されます（RECOMMENDED）。実装はこの上限を設定可能にすべきです（SHOULD）。上限を超えることになる[部分的な成功](#部分的な成功-1)のレスポンスについては、可能かつ実用的であれば、レスポンスの意味を変えずにレスポンスサイズを削減すべきです（SHOULD）。そのために、サーバーは`partial_success.error_message`のような任意の診断フィールドの詳細度を下げたり、任意の診断フィールドを省略したりしてもよい（MAY）です。それでもレスポンスが上限に収まらない場合、サーバーは`HTTP 500 Internal Server Error`でそのリクエストを失敗させなければなりません（MUST）。このような失敗の後、リクエストに含まれていたテレメトリーデータが受理されたかどうかは規定されません。

サーバーは、レスポンス本文がバイナリエンコードされたProtobufペイロードである場合、「Content-Type: application/x-protobuf」ヘッダーを設定しなければなりません（MUST）。サーバーは、レスポンスがJSONエンコードされたProtobufペイロードである場合、「Content-Type: application/json」を設定しなければなりません（MUST）。サーバーは、リクエストで受信した「Content-Type」と同じものをレスポンスで使わなければなりません（MUST）。

リクエストヘッダーに「Accept-Encoding: gzip」が存在する場合、サーバーはレスポンスをgzipエンコードし、「Content-Encoding: gzip」レスポンスヘッダーを設定してもよい（MAY）です。

##### 完全な成功

成功レスポンスは、テレメトリーデータがサーバーに正常に受理されたことを示します。

サーバーが空のリクエスト（テレメトリーデータを一切含まないリクエスト）を受信した場合、サーバーは成功で応答すべきです（SHOULD）。

成功時、サーバーは`HTTP 200 OK`で応答しなければなりません（MUST）。レスポンス本文は、Protobufでエンコードされた[Export\<signal>ServiceResponse](https://github.com/open-telemetry/opentelemetry-proto/tree/v1.11.0/opentelemetry/proto/collector)メッセージ（トレース用の`ExportTraceServiceResponse`、メトリクス用の`ExportMetricsServiceResponse`、ログ用の`ExportLogsServiceResponse`、プロファイル用の`ExportProfilesServiceResponse`）でなければなりません（MUST）。

サーバーは、成功レスポンスの場合、`partial_success`フィールドを未設定のままにしなければなりません（MUST）。

##### 部分的な成功

リクエストが部分的にのみ受理された場合（つまりサーバーがデータの一部のみを受理し、残りを拒否した場合）、サーバーは`HTTP 200 OK`で応答しなければなりません（MUST）。レスポンス本文は、[完全な成功](#完全な成功-1)のケースと同じ[Export\<signal>ServiceResponse](https://github.com/open-telemetry/opentelemetry-proto/tree/v1.11.0/opentelemetry/proto/collector)メッセージでなければなりません（MUST）。

さらに、サーバーは`partial_success`フィールド（トレース用の`ExportTracePartialSuccess`メッセージ、メトリクス用の`ExportMetricsPartialSuccess`メッセージ、ログ用の`ExportLogsPartialSuccess`メッセージ、プロファイル用の`ExportProfilesPartialSuccess`）を初期化しなければならず（MUST）、拒否したスパン・データポイント・ログレコードの数を、対応する`rejected_spans`、`rejected_data_points`、`rejected_log_records`または`rejected_profiles`フィールドに設定しなければなりません（MUST）。

サーバーは、`error_message`フィールドに英語での人間が読めるエラーメッセージを設定すべきです（SHOULD）。このメッセージでは、サーバーがデータの一部を拒否した理由を説明し、問題への対処方法についてのガイダンスを提供してもよいでしょう。本プロトコルはこのエラーメッセージの構造を定義しません。

サーバーは、リクエストを完全に受理した場合であっても、`partial_success`フィールドを使ってクライアントへ警告や提案を伝えてもよい（MAY）です。この場合、`rejected_<signal>`フィールドの値は`0`でなければならず（MUST）、`error_message`フィールドは空でないものでなければなりません（MUST）。

クライアントは、`partial_success`が設定された部分的な成功のレスポンスを受け取った場合、そのリクエストをリトライしてはなりません（MUST NOT）。

##### 失敗

リクエストの処理が失敗した場合、サーバーは適切な`HTTP 4xx`または`HTTP 5xx`ステータスコードで応答しなければなりません（MUST）。具体的な失敗ケースと使うべきHTTPステータスコードの詳細については、以下の節を参照してください。

すべての`HTTP 4xx`と`HTTP 5xx`レスポンスの本文は、問題を説明する、Protobufでエンコードされた[Status](https://godoc.org/google.golang.org/genproto/googleapis/rpc/status#Status)メッセージでなければなりません（MUST）。

本仕様書は`Status.code`フィールドを使わず、サーバーは`Status.code`フィールドを省略してもよい（MAY）です。クライアントは`Status.code`フィールドに基づいて挙動を変えることを期待されませんが、トラブルシューティングのためにこれを記録してもよい（MAY）です。

`Status.message`フィールドには、`Status`メッセージスキーマで定義された開発者向けのエラーメッセージを含めるべきです（SHOULD）。

サーバーは、追加の詳細を含む`Status.details`フィールドを含めてもよい（MAY）です。このフィールドが各失敗ケースで何を含められるかについては以下を参照してください。

サーバーは、特定のエラー状況に対してリトライ可能・リトライ不可能を示すためにHTTPレスポンスステータスコードを使うべきです（SHOULD）。クライアントは、HTTPレスポンスステータスコードをリトライ可能・リトライ不可能として尊重すべきです（SHOULD）。

##### リトライ可能なレスポンスコード

次の表に列挙されたレスポンスステータスコードを受け取ったリクエストはリトライされるべきです（SHOULD）。それ以外の`4xx`または`5xx`レスポンスステータスコードはリトライしてはなりません（MUST NOT）。

|HTTP response status code|
|---------|
|429 Too Many Requests|
|502 Bad Gateway|
|503 Service Unavailable|
|504 Gateway Timeout|

##### 不正なデータ

リクエストの処理が、デコードできない、または他の理由で無効なデータを含んでいたために失敗し、その失敗が永続的なものである場合、サーバーは`HTTP 400 Bad Request`で応答しなければなりません（MUST）。レスポンスの`Status.details`フィールドには、不正なデータを説明する[BadRequest](https://github.com/googleapis/googleapis/blob/d14bf59a446c14ef16e9931ebfc8e63ab549bf07/google/rpc/error_details.proto#L166)を含めるべきです（SHOULD）。

クライアントは、`HTTP 400 Bad Request`レスポンスを受け取った場合、そのリクエストをリトライしてはなりません（MUST NOT）。

##### OTLP/HTTPスロットリング

サーバーが、クライアントに許可された数を超えるリクエストを受信した場合、またはサーバーが過負荷になっている場合、サーバーは`HTTP 429 Too Many Requests`または`HTTP 503 Service Unavailable`で応答すべきで（SHOULD）、クライアントがリトライまでどれだけ待つべきかを示す["Retry-After"](https://tools.ietf.org/html/rfc7231#section-7.1.3)ヘッダーを含めてもよい（MAY）です。この値はHTTP日付形式、またはレスポンスを受信してから遅延させる秒数のいずれかであることに注意してください。

クライアントは、"Retry-After"ヘッダーが存在する場合、その値を尊重すべきです（SHOULD）。クライアントがリトライ可能なエラーコード（[上の表](#リトライ可能なレスポンスコード)を参照）を受け取り、レスポンスに"Retry-After"ヘッダーが存在しない場合、クライアントはリトライの間に指数バックオフ戦略を実装すべきです（SHOULD）。

##### その他すべてのレスポンス

本ドキュメントで明示的に列挙されていないその他すべてのHTTPレスポンスは、HTTP仕様に従って扱われるべきです。

サーバーがレスポンスを返さずに切断した場合、クライアントはリトライして同じリクエストを送信すべきです（SHOULD）。クライアントは、サーバーへの過負荷を避けるため、リトライの間に指数バックオフ戦略を実装すべきです（SHOULD）。

#### OTLP/HTTP接続

クライアントがサーバーに接続できない場合、クライアントはリトライの間に指数バックオフ戦略を使って接続をリトライすべきです（SHOULD）。リトライ間の間隔にはランダムなジッターを持たせなければなりません。

クライアントはリクエストの間、接続を維持すべきです（SHOULD）。

サーバー実装は、バイナリエンコードされたProtobufペイロードのOTLP/HTTPリクエストと、JSONエンコードされたProtobufペイロードのOTLP/HTTPリクエストを同じポートで受け付け、「Content-Type」リクエストヘッダーに基づいて対応するペイロードデコーダーへ多重化すべきです（SHOULD）。

サーバー実装は、OTLP/gRPCとOTLP/HTTPのリクエストを同じポートで受け付け、「Content-Type」リクエストヘッダーに基づいて対応するトランスポートハンドラーへ接続を多重化してもよい（MAY）です。

#### OTLP/HTTP同時リクエスト

より高い総スループットを達成するため、クライアントは複数の並列HTTP接続を使ってリクエストを送信してもよい（MAY）です。その場合、並列接続の最大数は設定可能であるべきです（SHOULD）。

#### OTLP/HTTPの既定のポート

OTLP/HTTPの既定のネットワークポートは4318です。

## 実装に関する推奨事項

### マルチデスティネーションエクスポート

1つのクライアントが複数の宛先サーバーへテレメトリーデータを送信しなければならない場合、追加の複雑さを考慮する必要があります。一方のサーバーがデータを確認応答し、もう一方のサーバーがまだ確認応答していない場合、クライアントはどのように先へ進めるかを決める必要があります。

このような状況では、クライアントは宛先ごとにキューイング・確認応答処理・リトライのロジックを実装すべきです（SHOULD）。これにより、サーバー同士が互いをブロックしないことが保証されます。キューは、複数のキューを持つことによるメモリオーバーヘッドを最小化するため、共有された不変のデータを参照すべきです（SHOULD）。

![マルチデスティネーションエクスポート](https://raw.githubusercontent.com/open-telemetry/opentelemetry-proto/v1.11.0/docs/img/otlp-multi-destination.png)

これにより、すべての宛先サーバーが、それぞれの受信速度にかかわらず（クライアント側のキューサイズによって課される利用可能な上限の範囲内で）データを受信できることが保証されます。

### 空のテレメトリーエンベロープ

特定の状況下では、内容が空のテレメトリーエンベロープが発生することがあります。例としては、ScopeMetricsを含まないResourceMetrics、Metricsを含まないResourceMetrics、あるいはLogsやSpansにおける同様のケースが考えられます。これが発生する1つの経路としては、フィルタリングルールがすべての含まれるデータポイントを取り除く場合が考えられますが、他にも原因はあります。

実際には、こうした空のエンベロープは既存の実装によってしばしば破棄されます。それを踏まえ、送信者は空のエンベロープ（スパン数・メトリクスポイント数・ログレコード数がゼロのOTLPペイロード）を作成すべきではなく（SHOULD NOT）、受信者は空のエンベロープを無視してもよく（MAY）、OTLPペイロードを受信して送信（転送）する実装は空のエンベロープを破棄してもよい（MAY）です。

## 既知の制限

### リクエストの確認応答

#### 重複データ

エッジケース（再接続時、ネットワーク中断時など）では、確認応答をまだ受け取っていない場合、クライアントは最近送信したデータが配送されたかどうかを知る方法がありません。クライアントは通常、配送を保証するためにそうしたデータを再送信することを選び、その結果サーバー側で重複データが生じる可能性があります。これは意図的な選択であり、テレメトリーデータにとって正しいトレードオフだと考えられています。

## 将来のバージョンと相互運用性

OTLPは時間の経過とともに進化し変化していきます。OTLPの将来のバージョンは、異なるバージョンのOTLPを実装するクライアントとサーバーが相互運用してテレメトリーデータを交換できることを保証するように設計・実装されなければなりません。古いクライアントは新しいサーバーと通信できなければならず、その逆もまた同様です。OTLPの新しいバージョンが、OTLPの古いバージョンを実装しているノードには理解・サポートできない新機能を導入する場合を考えます。その場合、プロトコルは機能的な観点から最小公倍数へ後退しなければなりません。

可能な限り、廃止が宣言されていないOTLPのすべてのバージョンの間で相互運用性が保証されなければなりません（MUST）。

OTLPは明示的なプロトコルバージョン番号を使いません。異なるバージョンのクライアントとサーバーのOTLPの相互運用性は、次の考え方に基づいています。

1. OTLP（現在および将来のバージョン）は一連のケーパビリティを定義し、その一部は必須で、他は任意です。クライアントとサーバーは必須のケーパビリティを実装しなければならず、任意のケーパビリティについてはその一部だけを実装することを選んでも構いません。

2. プロトコルへの小さな変更については、OTLPの将来のバージョンと拡張は、後方互換性のある方法でメッセージスキーマを進化させるProtobufの能力を使うことが推奨されます。OTLPの新しいバージョンは、それらのフィールドを理解しないクライアントやサーバーには無視されるような新しいフィールドをメッセージに追加できます。多くの場合、そうしたスキーマ変更を注意深く設計し、新しいフィールドの既定値を正しく選ぶことで、ピアノードが異なるケーパビリティを持つことを明示的に検出しなくても、異なるバージョン間の相互運用性を確保できます。

3. より重大な変更は、将来のOTEPにおいて新しい任意のケーパビリティとして明示的に定義されなければなりません。そうしたケーパビリティは、下層のトランスポートを確立した後にクライアントとサーバーの実装によって発見されるべきです（SHOULD）。具体的な発見の仕組みは将来のOTEPで記述されるべきで（SHOULD）、通常はクライアントからサーバーへの発見リクエスト・レスポンスのメッセージ交換を実装することで実現できます。新しいケーパビリティを定義するOTEPが記述します。本仕様書で定義された必須のケーパビリティは暗黙的であり、発見を必要としません。新しい任意のケーパビリティをサポートする実装は、その特定のケーパビリティをサポートしないピアの期待に合わせて挙動を調整しなければなりません（MUST）。

## 用語集

テレメトリーデータの交換には2つの当事者が関与します。本ドキュメントでは、テレメトリーデータの発生源となる当事者を`Client`、テレメトリーデータの宛先となる当事者を`Server`と呼びます。

![クライアント・サーバー](https://raw.githubusercontent.com/open-telemetry/opentelemetry-proto/v1.11.0/docs/img/otlp-client-server.png)

Clientの例は、計装対象のアプリケーションやテレメトリーコレクターの送信側であり、Serverの例は、テレメトリーバックエンドやテレメトリーコレクターの受信側です（つまりコレクターは、どちら側から見るかによって通常ClientとServerの両方になります）。

ClientとServerはどちらも`Node`でもあります。本ドキュメントでは、この用語をどちらか一方を指す場合に使います。

## 参考文献

- [OTEP 0035](https://github.com/open-telemetry/oteps/blob/main/text/0035-opentelemetry-protocol.md) OpenTelemetry Protocol Specification
- [OTEP 0099](https://github.com/open-telemetry/oteps/blob/main/text/0099-otlp-http.md) OTLP/HTTP: HTTP Transport Extension for OTLP
- [OTEP 0122](https://github.com/open-telemetry/oteps/blob/main/text/0122-otlp-http-json.md) OTLP: JSON Encoding for OTLP/HTTP

