> Source: https://www.ymotongpoo.com/works/oteps/otep-0016/

# OTEP-0016: 名前付きのトレーサーとメーター

_トレーサー（Tracer）またはメーター（Meter）を取得するためにライブラリが使用するAPIをパラメーター化することで、テレメトリーデータを報告する計装ライブラリの名前とバージョンにトレーサーとメーターを関連付けます。_

## 推薦参考文書

* [Proposal: Tracer Components](https://github.com/open-telemetry/opentelemetry-specification/issues/10)
* [Global Instance discussions](https://github.com/open-telemetry/opentelemetry-specification/labels/global%20instance)
* [Proposal: Add a version resource](https://github.com/open-telemetry/oteps/pull/38)

## 動機

ここで提案する「名前付きのトレーサーとメーター」のメカニズムは、次のようなシナリオを想定しています。

### 欠陥のある、あるいは高価な計装

OpenTelemetryを使っているアプリケーションの運用担当者にとって、計装ライブラリが生成するデータ量に影響を与える方法は今のところありません。
計装ライブラリは、簡単にバックエンドシステムを「スパム」したり、偽のデータを配信したり、最悪の場合、アプリケーションをクラッシュさせたり、低速化させたりします。
これらの問題は、負荷の増加や予期せぬ入力データなどの外部要因によって、本番環境で突然発生する可能性さえあります。

### 計装ライブラリの識別

計装ライブラリが[セマンティック規約](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/overview.md#semantic-conventions)を正しく実装していなかったり、それらの規約が時間の経過とともに変更された場合、そのライブラリによって生成されたデータを選択的に解釈し、サニタイズすることは現在のところ困難です。
生成されたスパンやメトリクスは、処理パイプラインやバックエンドのいずれにおいても、それらを報告したライブラリと後で関連付けることはできません。

### 計装済みライブラリの計装を無効にする

ライブラリのベンダーが OpenTelemetry API を実装し、そのライブラリを自動計装する必要性をなくすことが、OpenTelemetry の最終的なゴールです。
運用担当者は、あるデータベースドライバーや他のライブラリに組み込まれたテレメトリーを無効にし、組み込みのテレメトリーが何らかの形で欠けている場合、彼ら自身で統合できるようにすべきです。
これは、そのデータベースドライバーの開発者がテレメトリーを無効にする設定を提供していない場合でも可能であるべきです。

## 解決策

本提案は、このような問題点を解決するために、次のようなコンセプトを導入しようとするものです。

* 名前付きのトレーサーとメーターは、**名前** （例: _"io.opentelemetry.contrib.mongodb"_ ）と **バージョン** （例: _"semver:1.0.0"_ ）に関連付けられます。
* トレーサーやメーターを取得する唯一の手段は `TracerProvider` / `MeterProvider` です。

名前とバージョンに基づき、プロバイダーは、特定の計装ライブラリにno-opのトレーサーやメーターを提供したり、特定のライブラリからスパンやメトリクスを破棄するサンプラーを実装することができます。
また、カスタムエクスポーターを提供することで、スパンやメトリクスのデータをバックエンドシステムで処理する前にサニタイズできます。
しかし、これは基本的なメカニズムを提供するだけのこの提案の範囲を超えています。

## 解説

ユーザーの視点から見ると、_名前付きトレーサー／メーター_ と `TracerProvider` / `MeterProvider` での作業は、たとえば、[Java logging API](https://docs.oracle.com/javase/7/docs/api/java/util/logging/Logger.html#getLogger(java.lang.String)) や [log4j](https://www.slf4j.org/apidocs/org/slf4j/LoggerFactory.html) のようなロギングフレームワークの動作に概念的に似ています。
LoggerFactoriesを通してLoggerオブジェクトを要求するのと同様に、計装ライブラリは、TracerProvider / MeterProviderを通して特定のTracer / Meterオブジェクトを作成します。

新しいトレーサーやメーターは、計装ライブラリの名前とバージョンを指定することで作成できます。
バージョン（<https://github.com/open-telemetry/oteps/pull/38>で提案されている慣習にしたがう）は基本的にオプションですが、この情報のみで以下のシナリオが可能になるため、提供される _べき_ です。

* ある計装ライブラリの特定範囲のバージョンだけを抑制する必要があり、それ以外のバージョンは許容される（たとえば、その特定バージョンのバグが原因）。
* Go モジュールでは、1つのビルドで同じミドルウェアの複数のバージョンを使うことができるので、実行時にそれらを決定する必要があります。


```java
// Create a tracer/meter for a given instrumentation library in a specific version.
Tracer tracer = OpenTelemetry.getTracerProvider().getTracer("io.opentelemetry.contrib.mongodb", "semver:1.0.0");
Meter meter = OpenTelemetry.getMeterProvider().getMeter("io.opentelemetry.contrib.mongodb", "semver:1.0.0");
```

これらのファクトリー（`TracerProvider` と `MeterProvider`）は、グローバルな `Tracer` / `Meter` シングルトンオブジェクトに代わって、トレーサーとメーターのインスタンスを要求するためのユビキタスポイントとなります。

トレーサーやメーターの作成に使用される _名前_ は、計装されるライブラリではなく、_計装_ ライブラリ（_インテグレーション_ とも呼ばれます）を特定するものでなければなりません。
これらの計装ライブラリは、OpenTelemetry リポジトリで開発されたライブラリであったり、サードパーティーの実装であったり、あるいは自動注入コード（[手動計装をしない（オープン）テレメトリーのOTEP](./otep-0001) を参照）であったりします。
末尾の識別子の例も参照してください。

ライブラリ（またはアプリケーション）に計装が組み込まれている場合、そのライブラリは計装するライブラリであると同時に計装されたライブラリであり、ここに独自の名前を渡す必要があります。
それ以外のすべての場合（そしてその場合と区別するために）、計装するライブラリと計装されたライブラリの区別は非常に重要です。
たとえば、HTTP ライブラリ `com.example.http` が `io.opentelemetry.contrib.examplehttp` のどちらかによって計装されている場合、トレーサーは `com.example.http` という名前ではなく、実際の計装ライブラリの後に `io.opentelemetry.contrib.examplehttp` という名前を付けることが重要です。

名前（NULLまたは空文字列）が指定されない場合、[「エラー処理案」](https://github.com/open-telemetry/opentelemetry-specification/pull/153)の提案にしたがって、「スマートデフォルト」が適用され、デフォルトのトレーサー/メーター実装が返されます。

### トレーサーやメーターの名前の例

トレーサーやメーターの名前は、そのトレーサーやメーターを使用するライブラリを表すので、できるだけ一意な名前になるように定義する必要があります。
トレーサーやメーターの名前は、計装を提供するライブラリ、クラス、パッケージのアイデンティティを表すものであるべきです。

以下はOpenTracing と OpenCensus の既存の貢献ライブラリに基づいた例です。

* `io.opentracing.contrib.spring.rabbitmq`
* `io.opentracing.contrib.jdbc`
* `io.opentracing.thrift`
* `io.opentracing.contrib.asynchttpclient`
* `io.opencensus.contrib.http.servlet`
* `io.opencensus.contrib.spring.sleuth.v1x`
* `io.opencensus.contrib.http.jaxrs`
* `github.com/opentracing-contrib/go-amqp` (Go)
* `github.com/opentracing-contrib/go-grpc` (Go)
* `OpenTracing.Contrib.NetCore.AspNetCore` (.NET)
* `OpenTracing.Contrib.NetCore.EntityFrameworkCore` (.NET)

## 内部の詳細

`TracerProvider` / `MeterProvider` と _名前付きのトレーサーとメーター_ を提供することで、ベンダーや OpenTelemetry の実装は、トレーサーやメーターを提供したり、結果として生成されるスパンやメトリクスにどの属性を設定するかについて、より柔軟性を得られます。

SDKレベルでは、SpanDataクラスとそのMetricsクラスは `getLibraryResource` 関数で拡張されています。
これは、それを作成したトレーサーやメーターに関連付けられたリソースを返します。

## 用語集

### 計装ライブラリ

トレース/メトリクスレポーターとしても知られており、これは、既存のライブラリを計装する OpenTelemetry が提供するライブラリ／モジュール／プラグイン、何らかのライブラリを計装するサードパーティーのインテグレーション、あるいは、それ自身を計装するために OpenTelemetry API を実装したライブラリのいずれかになります。
いずれにせよ、計装ライブラリは、OpenTelemetry にトレースとメトリクスデータを提供するライブラリです。

以下は計装ライブラリの例です。

* `@opentelemetry/plugin-http`
* `io.opentelemetry.redis`
* `redis-client` （この場合、`redis-client` はOpenTelemetry APIで自分自身を計測しています）

### トレーサー／メーター名とバージョン

計装ライブラリがトレーサーやメーターを取得するとき、それ自身の名前とバージョンを Tracer Provider や Meter Provider に提供します。
この名前とバージョンの2つのタプルはトレーサーやメーターの _名前_ と _バージョン_ と呼ばれます。
これはトレーサーやメーターを取得するライブラリの名前とバージョンであり、監視対象のライブラリではないことに注意してください。
ライブラリが OpenTelemetry API を使ってそれ自身を計装している場合、これらは同じかもしれません。

たとえば、もし `http` ライブラリのバージョン `semver:3.0.0` が、名前 `io.opentelemetry.contrib.http` でバージョン `semver:1.3.2` のライブラリによって計装されている場合、トレーサー名とバージョンも `io.opentelemetry.contrib.http` と `semver:1.3.2` になります。
同じ `http` ライブラリに OpenTelemetry API を使用した組み込みの計装がある場合、トレーサー名とバージョンは `http` と `semver:3.0.0` となります。

### メーターの名前空間

メータ名は、それによって作成されるすべてのメトリクスの名前空間として使用されます。
これにより、テレメトリーライブラリは、別のライブラリによって同じ名前で登録されたメトリクスとの衝突を心配することなく、 `latency` のような任意の名前を使用してメトリクスを登録できます。

たとえば、ライブラリ `redis` と `io.opentelemetry.redis` は、どちらも `latency` という名前のメトリクスを登録できます。
これらのメトリクスは、異なる名前空間（それぞれ `redis`と `io.opentelemetry.redis`）に登録されているため、同じ名前であっても一意に識別できます。
この場合、これらのメトリクスは同じものを測定しているため、運用担当者はこれらのメトリクスのいずれかを無効にできます。

## 先行技術と代替技術

この提案は、[コンポーネント](https://github.com/open-telemetry/opentelemetry-specification/issues/10) に関する `opentelemetry-specification` 提案に由来します。
なぜなら、名前付きトレーサーという概念を持つことで、このセマンティックな `component` プロパティを自動的に決定することが可能になるからです。

あるいは、 `TracerProvider` を持つかわりに、既存の（グローバルな）トレーサーは、（たとえば `TraceComponent` と呼ばれる）追加の間接的なオブジェクトを返すことができます。これによって、特定の名前のトレースされたコンポーネントのスパンを生成できます。

```java
TraceComponent traceComponent = OpenTelemetry.Tracing.getTracer().componentBuilder("io.opentelemetry.contrib.mongodb", "semver:1.0.0");
Span span = traceComponent.spanBuilder("someMethod").startSpan();
```

実際のスパンを生成するまでの間接的なレベルは同じなので、全体的には `TracerProvider` と大きく変わることはないでしょう。

与えられたトレーサー名に基づいて `component` プロパティを設定するかわりに、それらの名前を生成されたスパン名の接頭辞として使用することもできます（たとえば、`<TracerName-SpanName>`）。
しかし、データ品質やセマンティック規約を考慮すると、スパンには専用の `component` セットを使用することが望ましいでしょう。

新しいトレーサーを作成するための引数としてプレーンな文字列を使用するかわりに、計装ライブラリを識別する `Resource` を使用できます。
このようなリソースは _version_ と _name_ ラベルを持たなければなりません (これらのラベルにはセマンティック規約が定義されているかもしれません)。
この実装の選択肢は主に API レベルで `Resource` データ型が利用できるかどうかに依存します（<https://github.com/open-telemetry/opentelemetry-specification/pull/254> を参照してください）。

```java
// Create resource for given instrumentation library information (name + version)
Map<String, String> libraryLabels = new HashMap<>();
libraryLabels.put("name", "io.opentelemetry.contrib.mongodb");
libraryLabels.put("version", "1.0.0");
Resource libraryResource = Resource.create(libraryLabels);
// Create tracer for given instrumentation library.
Tracer tracer = OpenTelemetry.getTracerProvider().getTracer(libraryResource);
```

これらの選択肢は、メーターとメトリクスにも同じように適用できます。

## 今後の可能性

トレーサーやメーターを特定するリソース情報に基づいて、プログラムまたは外部設定ソース（環境など）を介して、これらの設定（有効化／無効化）を行うことができます。

この提案に基づけば、将来の「シグナルプロデューサー」（つまりログ）は、同じか類似の作成アプローチを使用できます。

